File: | d/adpeval.c |
Warning: | line 1376, column 13 Access out-of-bound array element (buffer overflow) |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* | |||
2 | * The contents of this file are subject to the Mozilla Public License | |||
3 | * Version 1.1 (the "License"); you may not use this file except in | |||
4 | * compliance with the License. You may obtain a copy of the License at | |||
5 | * http://mozilla.org/. | |||
6 | * | |||
7 | * Software distributed under the License is distributed on an "AS IS" | |||
8 | * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See | |||
9 | * the License for the specific language governing rights and limitations | |||
10 | * under the License. | |||
11 | * | |||
12 | * The Original Code is AOLserver Code and related documentation | |||
13 | * distributed by AOL. | |||
14 | * | |||
15 | * The Initial Developer of the Original Code is America Online, | |||
16 | * Inc. Portions created by AOL are Copyright (C) 1999 America Online, | |||
17 | * Inc. All Rights Reserved. | |||
18 | * | |||
19 | * Alternatively, the contents of this file may be used under the terms | |||
20 | * of the GNU General Public License (the "GPL"), in which case the | |||
21 | * provisions of GPL are applicable instead of those above. If you wish | |||
22 | * to allow use of your version of this file only under the terms of the | |||
23 | * GPL and not to allow others to use your version of this file under the | |||
24 | * License, indicate your decision by deleting the provisions above and | |||
25 | * replace them with the notice and other provisions required by the GPL. | |||
26 | * If you do not delete the provisions above, a recipient may use your | |||
27 | * version of this file under either the License or the GPL. | |||
28 | */ | |||
29 | ||||
30 | /* | |||
31 | * adpeval.c -- | |||
32 | * | |||
33 | * ADP string and file eval. | |||
34 | */ | |||
35 | ||||
36 | #include "nsd.h" | |||
37 | ||||
38 | /* | |||
39 | * The following structure defines a cached ADP page result. A cached | |||
40 | * object is created by executing the non-cached code and saving the | |||
41 | * resulting output which may include embedded non-cached components | |||
42 | * (see NsTclAdpIncludeObjCmd for details). | |||
43 | */ | |||
44 | ||||
45 | typedef struct AdpCache { | |||
46 | int refcnt; /* Current interps using cached results. */ | |||
47 | Ns_Time expires; /* Expiration time of cached results. */ | |||
48 | AdpCode code; /* ADP code for cached result. */ | |||
49 | } AdpCache; | |||
50 | ||||
51 | /* | |||
52 | * The following structure defines a shared page in the ADP cache. The | |||
53 | * size of the object is extended to include the filename bytes. | |||
54 | */ | |||
55 | ||||
56 | typedef struct Page { | |||
57 | NsServer *servPtr; /* Page server context (reg tags, etc.) */ | |||
58 | Tcl_HashEntry *hPtr; /* Entry in shared table of all pages. */ | |||
59 | time_t mtime; /* Original modify time of file. */ | |||
60 | off_t size; /* Original size of file. */ | |||
61 | dev_t dev; /* Device and inode to try to catch modifications ... */ | |||
62 | ino_t ino; /* ...below the mtime granularity. */ | |||
63 | unsigned int flags; /* Flags used on last compile, e.g., SAFE. */ | |||
64 | int refcnt; /* Refcnt of current interps using page. */ | |||
65 | int evals; /* Count of page evaluations. */ | |||
66 | int cacheGen; /* Cache generation id. */ | |||
67 | AdpCache *cachePtr; /* Cached output. */ | |||
68 | AdpCode code; /* ADP code blocks. */ | |||
69 | bool_Bool locked; /* Page locked for cache update. */ | |||
70 | } Page; | |||
71 | ||||
72 | /* | |||
73 | * The following structure holds per-interp script byte codes. The | |||
74 | * size of the object is extended based on the number of script objects. | |||
75 | */ | |||
76 | ||||
77 | typedef struct Objs { | |||
78 | int nobjs; /* Number of scripts objects. */ | |||
79 | Tcl_Obj *objs[1]; /* Scripts to be compiled and reused. */ | |||
80 | } Objs; | |||
81 | ||||
82 | /* | |||
83 | * The following structure defines a per-interp page entry with | |||
84 | * a pointer to the shared Page and private Objs for cached and | |||
85 | * non-cached page results. | |||
86 | */ | |||
87 | ||||
88 | typedef struct InterpPage { | |||
89 | Page *pagePtr; /* Pointer to shared page text. */ | |||
90 | Objs *objs; /* Non-cache ADP code script. */ | |||
91 | int cacheGen; /* Cache generation id. */ | |||
92 | Objs *cacheObjs; /* Cache results ADP code scripts. */ | |||
93 | } InterpPage; | |||
94 | ||||
95 | /* | |||
96 | * Local functions defined in this file. | |||
97 | */ | |||
98 | ||||
99 | static Page *ParseFile(const NsInterp *itPtr, const char *file, struct stat *stPtr, unsigned int flags) | |||
100 | NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2))) NS_GNUC_NONNULL(3)__attribute__((__nonnull__(3))); | |||
101 | ||||
102 | static int AdpEval(NsInterp *itPtr, int objc, Tcl_Obj *const* objv, const char *resvar) | |||
103 | NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))); | |||
104 | ||||
105 | static int AdpExec(NsInterp *itPtr, int objc, Tcl_Obj *const* objv, const char *file, | |||
106 | const AdpCode *codePtr, Objs *objsPtr, Tcl_DString *outputPtr, | |||
107 | const struct stat *stPtr) | |||
108 | NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(5)__attribute__((__nonnull__(5))) NS_GNUC_NONNULL(7)__attribute__((__nonnull__(7))); | |||
109 | ||||
110 | static int AdpSource(NsInterp *itPtr, int objc, Tcl_Obj *const* objv, const char *file, | |||
111 | const Ns_Time *expiresPtr, Tcl_DString *outputPtr) | |||
112 | NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(4)__attribute__((__nonnull__(4))) NS_GNUC_NONNULL(6)__attribute__((__nonnull__(6))); | |||
113 | ||||
114 | static int AdpDebug(const NsInterp *itPtr, const char *ptr, int len, int nscript) | |||
115 | NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2))); | |||
116 | ||||
117 | static void DecrCache(AdpCache *cachePtr) | |||
118 | NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))); | |||
119 | ||||
120 | static Objs *AllocObjs(int nobjs); | |||
121 | ||||
122 | static void FreeObjs(Objs *objsPtr) | |||
123 | NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))); | |||
124 | ||||
125 | static void AdpTrace(const NsInterp *itPtr, const char *ptr, int len) | |||
126 | NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2))); | |||
127 | ||||
128 | static Ns_Callback FreeInterpPage; | |||
129 | static Ns_ServerInitProc ConfigServerAdp; | |||
130 | ||||
131 | ||||
132 | /* | |||
133 | *---------------------------------------------------------------------- | |||
134 | * | |||
135 | * NsConfigAdp -- | |||
136 | * | |||
137 | * Initialize and configure the ADP subsystem. | |||
138 | * | |||
139 | * Results: | |||
140 | * None. | |||
141 | * | |||
142 | * Side effects: | |||
143 | * None. | |||
144 | * | |||
145 | *---------------------------------------------------------------------- | |||
146 | */ | |||
147 | ||||
148 | void | |||
149 | NsConfigAdp(void) | |||
150 | { | |||
151 | NsRegisterServerInit(ConfigServerAdp); | |||
152 | } | |||
153 | ||||
154 | static Ns_ReturnCode | |||
155 | ConfigServerAdp(const char *server) | |||
156 | { | |||
157 | NsServer *servPtr = NsGetServer(server); | |||
158 | const char *path; | |||
159 | ||||
160 | path = Ns_ConfigSectionPath(NULL((void*)0), server, NULL((void*)0), "adp", (char *)0L); | |||
161 | ||||
162 | /* | |||
163 | * Initialize the page and tag tables and locks. | |||
164 | */ | |||
165 | ||||
166 | Tcl_InitHashTable(&servPtr->adp.pages, TCL_STRING_KEYS(0)); | |||
167 | Tcl_InitHashTable(&servPtr->adp.tags, TCL_STRING_KEYS(0)); | |||
168 | ||||
169 | Ns_CondInit(&servPtr->adp.pagecond); | |||
170 | ||||
171 | Ns_MutexInit(&servPtr->adp.pagelock); | |||
172 | Ns_MutexSetName2(&servPtr->adp.pagelock, "ns:adp:pages", server); | |||
173 | ||||
174 | Ns_RWLockInit(&servPtr->adp.taglock); | |||
175 | Ns_RWLockSetName2(&servPtr->adp.taglock, "rw:adp:tags", server); | |||
176 | ||||
177 | /* | |||
178 | * Initialise various ADP options. | |||
179 | */ | |||
180 | ||||
181 | servPtr->adp.errorpage = Ns_ConfigString(path, "errorpage", NULL((void*)0)); | |||
182 | servPtr->adp.startpage = Ns_ConfigString(path, "startpage", NULL((void*)0)); | |||
183 | servPtr->adp.debuginit = Ns_ConfigString(path, "debuginit", "ns_adp_debuginit"); | |||
184 | servPtr->adp.tracesize = Ns_ConfigInt(path, "tracesize", 40); | |||
185 | servPtr->adp.cachesize = (size_t)Ns_ConfigMemUnitRange(path, "cachesize", "5MB", 5000 * 1024, | |||
186 | 1000 * 1024, INT_MAX2147483647); | |||
187 | servPtr->adp.bufsize = (size_t)Ns_ConfigMemUnitRange(path, "bufsize", "1MB", 1024 * 1000, | |||
188 | 100 * 1024, INT_MAX2147483647); | |||
189 | servPtr->adp.defaultExtension = Ns_ConfigString(path, "defaultextension", NULL((void*)0)); | |||
190 | ||||
191 | servPtr->adp.flags = 0u; | |||
192 | (void) Ns_ConfigFlag(path, "cache", ADP_CACHE0x10u, 0, &servPtr->adp.flags); | |||
193 | (void) Ns_ConfigFlag(path, "stream", ADP_STREAM0x8000u, 0, &servPtr->adp.flags); | |||
194 | (void) Ns_ConfigFlag(path, "enableexpire", ADP_EXPIRE0x08u, 0, &servPtr->adp.flags); | |||
195 | (void) Ns_ConfigFlag(path, "enabledebug", ADP_DEBUG0x04u, 0, &servPtr->adp.flags); | |||
196 | (void) Ns_ConfigFlag(path, "safeeval", ADP_SAFE0x01u, 0, &servPtr->adp.flags); | |||
197 | (void) Ns_ConfigFlag(path, "singlescript", ADP_SINGLE0x02u, 0, &servPtr->adp.flags); | |||
198 | (void) Ns_ConfigFlag(path, "trace", ADP_TRACE0x20u, 0, &servPtr->adp.flags); | |||
199 | (void) Ns_ConfigFlag(path, "detailerror", ADP_DETAIL0x80u, 1, &servPtr->adp.flags); | |||
200 | (void) Ns_ConfigFlag(path, "stricterror", ADP_STRICT0x100u, 0, &servPtr->adp.flags); | |||
201 | (void) Ns_ConfigFlag(path, "displayerror", ADP_DISPLAY0x200u, 0, &servPtr->adp.flags); | |||
202 | (void) Ns_ConfigFlag(path, "trimspace", ADP_TRIM0x400u, 0, &servPtr->adp.flags); | |||
203 | (void) Ns_ConfigFlag(path, "autoabort", ADP_AUTOABORT0x2000u, 1, &servPtr->adp.flags); | |||
204 | ||||
205 | return NS_OK; | |||
206 | } | |||
207 | ||||
208 | ||||
209 | /* | |||
210 | *---------------------------------------------------------------------- | |||
211 | * | |||
212 | * NsAdpEval, NsAdpSource -- | |||
213 | * | |||
214 | * Evaluate an ADP string or file and return the output | |||
215 | * as the interp result. | |||
216 | * | |||
217 | * Results: | |||
218 | * A standard Tcl result. | |||
219 | * | |||
220 | * Side effects: | |||
221 | * Variable named by resvar, if any, is updated with results of | |||
222 | * Tcl interp before being replaced with ADP output. | |||
223 | * | |||
224 | *---------------------------------------------------------------------- | |||
225 | */ | |||
226 | ||||
227 | int | |||
228 | NsAdpEval(NsInterp *itPtr, int objc, Tcl_Obj *const* objv, const char *resvar) | |||
229 | { | |||
230 | NS_NONNULL_ASSERT(itPtr != NULL)((void) (0)); | |||
231 | ||||
232 | return AdpEval(itPtr, objc, objv, resvar); | |||
233 | } | |||
234 | ||||
235 | int | |||
236 | NsAdpSource(NsInterp *itPtr, int objc, Tcl_Obj *const* objv, const char *resvar) | |||
237 | { | |||
238 | NS_NONNULL_ASSERT(itPtr != NULL)((void) (0)); | |||
239 | ||||
240 | itPtr->adp.flags |= ADP_ADPFILE0x4000u; | |||
241 | return AdpEval(itPtr, objc, objv, resvar); | |||
242 | } | |||
243 | ||||
244 | static int | |||
245 | AdpEval(NsInterp *itPtr, int objc, Tcl_Obj *const* objv, const char *resvar) | |||
246 | { | |||
247 | Tcl_Interp *interp; | |||
248 | AdpCode code; | |||
249 | Tcl_DString output; | |||
250 | int result; | |||
251 | char *obj0; | |||
252 | ||||
253 | NS_NONNULL_ASSERT(itPtr != NULL)((void) (0)); | |||
254 | ||||
255 | interp = itPtr->interp; | |||
256 | /* | |||
257 | * If the ADP object is a file, simply source it. Otherwise, parse | |||
258 | * the script as a temporary ADP code object and execute it directly. | |||
259 | */ | |||
260 | ||||
261 | Tcl_DStringInit(&output); | |||
262 | obj0 = Tcl_GetString(objv[0]); | |||
263 | if ((itPtr->adp.flags & ADP_ADPFILE0x4000u) != 0u) { | |||
264 | result = AdpSource(itPtr, objc, objv, obj0, NULL((void*)0), &output); | |||
265 | } else { | |||
266 | NsAdpParse(&code, itPtr->servPtr, obj0, itPtr->adp.flags, NULL((void*)0)); | |||
267 | result = AdpExec(itPtr, objc, objv, NULL((void*)0), &code, NULL((void*)0), &output, NULL((void*)0)); | |||
268 | NsAdpFreeCode(&code); | |||
269 | } | |||
270 | ||||
271 | /* | |||
272 | * Set the interp result with the ADP output, saving the last interp | |||
273 | * result first if requested. | |||
274 | */ | |||
275 | ||||
276 | if (result == TCL_OK0) { | |||
277 | Tcl_Obj *objPtr; | |||
278 | ||||
279 | if (resvar != NULL((void*)0)) { | |||
280 | objPtr = Tcl_GetObjResult(interp); | |||
281 | if (Tcl_SetVar2Ex(interp, resvar, NULL((void*)0), objPtr, TCL_LEAVE_ERR_MSG0x200) == NULL((void*)0)) { | |||
282 | result = TCL_ERROR1; | |||
283 | } | |||
284 | } | |||
285 | if (result == TCL_OK0) { | |||
286 | objPtr = Tcl_NewStringObj(output.string, output.length); | |||
287 | Tcl_SetObjResult(interp, objPtr); | |||
288 | } | |||
289 | } | |||
290 | Tcl_DStringFree(&output); | |||
291 | ||||
292 | return result; | |||
293 | } | |||
294 | ||||
295 | ||||
296 | /* | |||
297 | *---------------------------------------------------------------------- | |||
298 | * | |||
299 | * NsAdpInclude -- | |||
300 | * | |||
301 | * Evaluate an ADP file, utilizing per-thread byte-code pages. | |||
302 | * | |||
303 | * Results: | |||
304 | * A standard Tcl result. | |||
305 | * | |||
306 | * Side effects: | |||
307 | * Output is either left in current ADP buffer. | |||
308 | * | |||
309 | *---------------------------------------------------------------------- | |||
310 | */ | |||
311 | ||||
312 | int | |||
313 | NsAdpInclude(NsInterp *itPtr, int objc, Tcl_Obj *const* objv, const char *file, const Ns_Time *expiresPtr) | |||
314 | { | |||
315 | Ns_DStringTcl_DString *outputPtr; | |||
316 | ||||
317 | NS_NONNULL_ASSERT(itPtr != NULL)((void) (0)); | |||
318 | NS_NONNULL_ASSERT(file != NULL)((void) (0)); | |||
319 | ||||
320 | /* | |||
321 | * If an ADP execution is already active, use the current output | |||
322 | * buffer. Otherwise, use the top-level buffer in the ADP struct. | |||
323 | */ | |||
324 | ||||
325 | if (itPtr->adp.framePtr != NULL((void*)0)) { | |||
326 | outputPtr = itPtr->adp.framePtr->outputPtr; | |||
327 | } else { | |||
328 | outputPtr = &itPtr->adp.output; | |||
329 | } | |||
330 | return AdpSource(itPtr, objc, objv, file, expiresPtr, outputPtr); | |||
331 | } | |||
332 | ||||
333 | ||||
334 | /* | |||
335 | *---------------------------------------------------------------------- | |||
336 | * | |||
337 | * NsAdpInit, NsAdpFree -- | |||
338 | * | |||
339 | * Initialize or free the NsInterp ADP data structures. | |||
340 | * | |||
341 | * Results: | |||
342 | * None. | |||
343 | * | |||
344 | * Side effects: | |||
345 | * None. | |||
346 | * | |||
347 | *---------------------------------------------------------------------- | |||
348 | */ | |||
349 | ||||
350 | void | |||
351 | NsAdpInit(NsInterp *itPtr) | |||
352 | { | |||
353 | NS_NONNULL_ASSERT(itPtr != NULL)((void) (0)); | |||
354 | ||||
355 | Tcl_DStringInit(&itPtr->adp.output); | |||
356 | NsAdpReset(itPtr); | |||
357 | } | |||
358 | ||||
359 | void | |||
360 | NsAdpFree(NsInterp *itPtr) | |||
361 | { | |||
362 | NS_NONNULL_ASSERT(itPtr != NULL)((void) (0)); | |||
363 | ||||
364 | if (itPtr->adp.cache != NULL((void*)0)) { | |||
365 | Ns_CacheDestroy(itPtr->adp.cache); | |||
366 | itPtr->adp.cache = NULL((void*)0); | |||
367 | } | |||
368 | Tcl_DStringFree(&itPtr->adp.output); | |||
369 | } | |||
370 | ||||
371 | ||||
372 | /* | |||
373 | *---------------------------------------------------------------------- | |||
374 | * | |||
375 | * NsAdpReset -- | |||
376 | * | |||
377 | * Reset the NsInterp ADP data structures for the next | |||
378 | * execution request. | |||
379 | * | |||
380 | * Results: | |||
381 | * None. | |||
382 | * | |||
383 | * Side effects: | |||
384 | * None. | |||
385 | * | |||
386 | *---------------------------------------------------------------------- | |||
387 | */ | |||
388 | ||||
389 | void | |||
390 | NsAdpReset(NsInterp *itPtr) | |||
391 | { | |||
392 | NS_NONNULL_ASSERT(itPtr != NULL)((void) (0)); | |||
393 | ||||
394 | itPtr->adp.exception = ADP_OK; | |||
395 | itPtr->adp.debugLevel = 0; | |||
396 | itPtr->adp.debugInit = 0; | |||
397 | itPtr->adp.debugFile = NULL((void*)0); | |||
398 | itPtr->adp.chan = NULL((void*)0); | |||
399 | itPtr->adp.conn = NULL((void*)0); | |||
400 | if (itPtr->servPtr != NULL((void*)0)) { | |||
401 | itPtr->adp.bufsize = itPtr->servPtr->adp.bufsize; | |||
402 | itPtr->adp.flags = itPtr->servPtr->adp.flags; | |||
403 | } else { | |||
404 | itPtr->adp.bufsize = 1024u * 1000u; | |||
405 | itPtr->adp.flags = 0u; | |||
406 | } | |||
407 | Tcl_DStringSetLength(&itPtr->adp.output, 0); | |||
408 | } | |||
409 | ||||
410 | /* | |||
411 | *---------------------------------------------------------------------- | |||
412 | * | |||
413 | * AdpSource -- | |||
414 | * | |||
415 | * Execute ADP code in a file with results returned in given | |||
416 | * dstring. | |||
417 | * | |||
418 | * Results: | |||
419 | * TCL_ERROR if the file could not be parsed, result of | |||
420 | * AdpExec otherwise. | |||
421 | * | |||
422 | * Side effects: | |||
423 | * Page text and ADP code results may be cached up to given time | |||
424 | * limit, if any. | |||
425 | * | |||
426 | *---------------------------------------------------------------------- | |||
427 | */ | |||
428 | ||||
429 | static int | |||
430 | AdpSource(NsInterp *itPtr, int objc, Tcl_Obj *const* objv, const char *file, | |||
431 | const Ns_Time *expiresPtr, Tcl_DString *outputPtr) | |||
432 | { | |||
433 | NsServer *servPtr; | |||
434 | Tcl_Interp *interp; | |||
435 | Tcl_HashEntry *hPtr; | |||
436 | struct stat st; | |||
437 | Ns_DStringTcl_DString tmp, path; | |||
438 | InterpPage *ipagePtr = NULL((void*)0); | |||
439 | Page *pagePtr = NULL((void*)0); | |||
440 | Ns_Time now; | |||
441 | int isNew; | |||
442 | const char *p; | |||
443 | int result = TCL_ERROR1; /* assume error until accomplished success */ | |||
444 | ||||
445 | NS_NONNULL_ASSERT(itPtr != NULL)((void) (0)); | |||
446 | NS_NONNULL_ASSERT(file != NULL)((void) (0)); | |||
447 | NS_NONNULL_ASSERT(outputPtr != NULL)((void) (0)); | |||
448 | ||||
449 | servPtr = itPtr->servPtr; | |||
450 | interp = itPtr->interp; | |||
451 | ||||
452 | Ns_DStringInitTcl_DStringInit(&tmp); | |||
453 | Ns_DStringInitTcl_DStringInit(&path); | |||
454 | ||||
455 | /* | |||
456 | * Construct the full, normalized path to the ADP file. | |||
457 | */ | |||
458 | ||||
459 | if (Ns_PathIsAbsolute(file) == NS_FALSE0) { | |||
460 | if (itPtr->adp.cwd == NULL((void*)0)) { | |||
461 | file = Ns_PagePath(&tmp, servPtr->server, file, (char *)0L); | |||
462 | } else { | |||
463 | file = Ns_MakePath(&tmp, itPtr->adp.cwd, file, (char *)0L); | |||
464 | } | |||
465 | } | |||
466 | file = Ns_NormalizePath(&path, file); | |||
467 | Ns_DStringSetLengthTcl_DStringSetLength(&tmp, 0); | |||
468 | ||||
469 | /* | |||
470 | * Check for TclPro debugging. | |||
471 | */ | |||
472 | ||||
473 | if (itPtr->adp.debugLevel > 0) { | |||
474 | ++itPtr->adp.debugLevel; | |||
475 | } else if ((itPtr->conn != NULL((void*)0)) | |||
476 | && ((itPtr->adp.flags & ADP_DEBUG0x04u) != 0u) | |||
477 | && itPtr->adp.debugFile != NULL((void*)0) | |||
478 | && (p = strrchr(file, INTCHAR('/')((int)((unsigned char)(('/')))))) != NULL((void*)0) | |||
479 | && Tcl_StringMatch(p+1, itPtr->adp.debugFile) != 0) { | |||
480 | const Ns_Set *hdrs; | |||
481 | const char *host, *port, *procs; | |||
482 | ||||
483 | hdrs = Ns_ConnGetQuery(interp, itPtr->conn, NULL((void*)0), NULL((void*)0)); /* currently ignoring encoding errors */ | |||
484 | host = Ns_SetIGet(hdrs, "dhost"); | |||
485 | port = Ns_SetIGet(hdrs, "dport"); | |||
486 | procs = Ns_SetIGet(hdrs, "dprocs"); | |||
487 | if (NsAdpDebug(itPtr, host, port, procs) != TCL_OK0) { | |||
488 | (void) Ns_ConnReturnNotice(itPtr->conn, 200, "Debug Init Failed", | |||
489 | Tcl_GetStringResult(interp)); | |||
490 | itPtr->adp.exception = ADP_ABORT; | |||
491 | goto done; | |||
492 | } | |||
493 | } | |||
494 | ||||
495 | if (itPtr->adp.cache == NULL((void*)0)) { | |||
496 | Ns_DStringPrintf(&tmp, "nsadp:%p", (void *)itPtr); | |||
497 | itPtr->adp.cache = Ns_CacheCreateSz(tmp.string, TCL_STRING_KEYS(0), | |||
498 | itPtr->servPtr->adp.cachesize, FreeInterpPage); | |||
499 | Ns_DStringSetLengthTcl_DStringSetLength(&tmp, 0); | |||
500 | } | |||
501 | ||||
502 | /* | |||
503 | * Verify the file is an existing, ordinary file and get page code. | |||
504 | */ | |||
505 | ||||
506 | if (stat(file, &st) != 0) { | |||
507 | Ns_TclPrintfResult(interp, "could not stat \"%s\": %s", | |||
508 | file, Tcl_PosixError(interp)); | |||
509 | } else if (!S_ISREG(st.st_mode)((((st.st_mode)) & 0170000) == (0100000))) { | |||
510 | Ns_TclPrintfResult(interp, "not an ordinary file: %s", file); | |||
511 | } else { | |||
512 | Ns_Entry *ePtr; | |||
513 | ||||
514 | /* | |||
515 | * Check for valid code in interp page cache. | |||
516 | */ | |||
517 | ||||
518 | ePtr = Ns_CacheFindEntry(itPtr->adp.cache, file); | |||
519 | if (ePtr != NULL((void*)0)) { | |||
520 | ipagePtr = Ns_CacheGetValue(ePtr); | |||
521 | if (ipagePtr->pagePtr->mtime != st.st_mtimest_mtim.tv_sec | |||
522 | || ipagePtr->pagePtr->size != st.st_size | |||
523 | || ipagePtr->pagePtr->dev != st.st_dev | |||
524 | || ipagePtr->pagePtr->ino != st.st_ino | |||
525 | || ipagePtr->pagePtr->flags != itPtr->adp.flags) { | |||
526 | Ns_CacheFlushEntry(ePtr); | |||
527 | ipagePtr = NULL((void*)0); | |||
528 | } | |||
529 | } | |||
530 | if (ipagePtr == NULL((void*)0)) { | |||
531 | ||||
532 | /* | |||
533 | * Find or create valid page in server table. | |||
534 | */ | |||
535 | ||||
536 | Ns_MutexLock(&servPtr->adp.pagelock); | |||
537 | hPtr = Tcl_CreateHashEntry(&servPtr->adp.pages, file, &isNew)(*((&servPtr->adp.pages)->createProc))(&servPtr ->adp.pages, (const char *)(file), &isNew); | |||
538 | while (isNew == 0 && (pagePtr = Tcl_GetHashValue(hPtr)((hPtr)->clientData)) == NULL((void*)0)) { | |||
539 | /* NB: Wait for other thread to read/parse page. */ | |||
540 | Ns_CondWait(&servPtr->adp.pagecond, &servPtr->adp.pagelock); | |||
541 | hPtr = Tcl_CreateHashEntry(&servPtr->adp.pages, file, &isNew)(*((&servPtr->adp.pages)->createProc))(&servPtr ->adp.pages, (const char *)(file), &isNew); | |||
542 | } | |||
543 | if (isNew == 0 && (pagePtr->mtime != st.st_mtimest_mtim.tv_sec | |||
544 | || pagePtr->size != st.st_size | |||
545 | || pagePtr->dev != st.st_dev | |||
546 | || pagePtr->ino != st.st_ino | |||
547 | || pagePtr->flags != itPtr->adp.flags)) { | |||
548 | /* NB: Clear entry to indicate read/parse in progress. */ | |||
549 | Tcl_SetHashValue(hPtr, NULL)((hPtr)->clientData = (ClientData) (((void*)0))); | |||
550 | pagePtr->hPtr = NULL((void*)0); | |||
551 | isNew = 1; | |||
552 | } | |||
553 | if (isNew != 0) { | |||
554 | Ns_MutexUnlock(&servPtr->adp.pagelock); | |||
555 | pagePtr = ParseFile(itPtr, file, &st, itPtr->adp.flags); | |||
556 | Ns_MutexLock(&servPtr->adp.pagelock); | |||
557 | if (pagePtr == NULL((void*)0)) { | |||
558 | Tcl_DeleteHashEntry(hPtr); | |||
559 | } else { | |||
560 | pagePtr->hPtr = hPtr; | |||
561 | Tcl_SetHashValue(hPtr, pagePtr)((hPtr)->clientData = (ClientData) (pagePtr)); | |||
562 | } | |||
563 | Ns_CondBroadcast(&servPtr->adp.pagecond); | |||
564 | } | |||
565 | if (pagePtr != NULL((void*)0)) { | |||
566 | ++pagePtr->refcnt; | |||
567 | } | |||
568 | Ns_MutexUnlock(&servPtr->adp.pagelock); | |||
569 | if (pagePtr != NULL((void*)0)) { | |||
570 | ipagePtr = ns_malloc(sizeof(InterpPage)); | |||
571 | ipagePtr->pagePtr = pagePtr; | |||
572 | ipagePtr->cacheGen = 0; | |||
573 | ipagePtr->objs = AllocObjs(pagePtr->code.nscripts); | |||
574 | ipagePtr->cacheObjs = NULL((void*)0); | |||
575 | ePtr = Ns_CacheCreateEntry(itPtr->adp.cache, file, &isNew); | |||
576 | if (isNew == 0) { | |||
577 | Ns_CacheUnsetValue(ePtr); | |||
578 | } | |||
579 | Ns_CacheSetValueSz(ePtr, ipagePtr, | |||
580 | (size_t) ipagePtr->pagePtr->size); | |||
581 | } | |||
582 | } | |||
583 | } | |||
584 | ||||
585 | /* | |||
586 | * If valid page was found, evaluate it in a new call frame. | |||
587 | */ | |||
588 | ||||
589 | if (ipagePtr != NULL((void*)0)) { | |||
590 | const AdpCode *codePtr; | |||
591 | Objs *objsPtr; | |||
592 | int cacheGen = 0; | |||
593 | AdpCache *cachePtr; | |||
594 | ||||
595 | pagePtr = ipagePtr->pagePtr; | |||
596 | if (expiresPtr == NULL((void*)0) || (itPtr->adp.flags & ADP_CACHE0x10u) == 0u) { | |||
597 | cachePtr = NULL((void*)0); | |||
598 | } else { | |||
599 | ||||
600 | Ns_MutexLock(&servPtr->adp.pagelock); | |||
601 | ||||
602 | /* | |||
603 | * First, wait for an initial cache if already executing. | |||
604 | */ | |||
605 | ||||
606 | while ((cachePtr = pagePtr->cachePtr) == NULL((void*)0) && pagePtr->locked) { | |||
607 | Ns_CondWait(&servPtr->adp.pagecond, &servPtr->adp.pagelock); | |||
608 | } | |||
609 | ||||
610 | /* | |||
611 | * Next, if a cache exists and isn't locked, check expiration. | |||
612 | */ | |||
613 | ||||
614 | if (cachePtr != NULL((void*)0) && ! pagePtr->locked) { | |||
615 | Ns_GetTime(&now); | |||
616 | if (Ns_DiffTime(&cachePtr->expires, &now, NULL((void*)0)) < 0) { | |||
617 | pagePtr->locked = NS_TRUE1; | |||
618 | cachePtr = NULL((void*)0); | |||
619 | } | |||
620 | } | |||
621 | ||||
622 | /* | |||
623 | * Create the cached page if necessary. | |||
624 | */ | |||
625 | ||||
626 | if (cachePtr == NULL((void*)0)) { | |||
627 | Ns_MutexUnlock(&servPtr->adp.pagelock); | |||
628 | codePtr = &pagePtr->code; | |||
629 | ++itPtr->adp.refresh; | |||
630 | result = AdpExec(itPtr, objc, objv, file, codePtr, ipagePtr->objs, | |||
631 | &tmp, &st); | |||
632 | --itPtr->adp.refresh; | |||
633 | ||||
634 | /* | |||
635 | * Check cache flag here one more time as we might clear it | |||
636 | * inside the script | |||
637 | */ | |||
638 | ||||
639 | if (result == TCL_OK0 && (itPtr->adp.flags & ADP_CACHE0x10u) != 0u) { | |||
640 | cachePtr = ns_malloc(sizeof(AdpCache)); | |||
641 | ||||
642 | /* | |||
643 | * Turn off Tcl mode after cached result, in caching mode | |||
644 | * we wrap Tcl file into proc 'adp:filename' and return | |||
645 | * as result only | |||
646 | * ns_adp_append {<% adp:filename %>} | |||
647 | * | |||
648 | * The output will be cached as result and every time we call | |||
649 | * that Tcl file, cached command will be executed as long as | |||
650 | * file is unchanged, if modified then the file will be reloaded, | |||
651 | * recompiled into same Tcl proc and cached | |||
652 | */ | |||
653 | ||||
654 | NsAdpParse(&cachePtr->code, itPtr->servPtr, tmp.string, | |||
655 | itPtr->adp.flags & ~ADP_TCLFILE0x10000u, file); | |||
656 | Ns_GetTime(&cachePtr->expires); | |||
657 | Ns_IncrTime(&cachePtr->expires, expiresPtr->sec, expiresPtr->usec); | |||
658 | cachePtr->refcnt = 1; | |||
659 | } | |||
660 | Ns_DStringSetLengthTcl_DStringSetLength(&tmp, 0); | |||
661 | Ns_MutexLock(&servPtr->adp.pagelock); | |||
662 | if (cachePtr != NULL((void*)0)) { | |||
663 | if (pagePtr->cachePtr != NULL((void*)0)) { | |||
664 | DecrCache(pagePtr->cachePtr); | |||
665 | } | |||
666 | ++pagePtr->cacheGen; | |||
667 | pagePtr->cachePtr = cachePtr; | |||
668 | } | |||
669 | pagePtr->locked = NS_FALSE0; | |||
670 | Ns_CondBroadcast(&servPtr->adp.pagecond); | |||
671 | } | |||
672 | cacheGen = pagePtr->cacheGen; | |||
673 | if (cachePtr != NULL((void*)0)) { | |||
674 | ++cachePtr->refcnt; | |||
675 | } | |||
676 | Ns_MutexUnlock(&servPtr->adp.pagelock); | |||
677 | } | |||
678 | if (cachePtr == NULL((void*)0)) { | |||
679 | codePtr = &pagePtr->code; | |||
680 | objsPtr = ipagePtr->objs; | |||
681 | } else { | |||
682 | codePtr = &cachePtr->code; | |||
683 | if (ipagePtr->cacheObjs != NULL((void*)0) && cacheGen != ipagePtr->cacheGen) { | |||
684 | FreeObjs(ipagePtr->cacheObjs); | |||
685 | ipagePtr->cacheObjs = NULL((void*)0); | |||
686 | } | |||
687 | if (ipagePtr->cacheObjs == NULL((void*)0)) { | |||
688 | ipagePtr->cacheObjs = AllocObjs(AdpCodeScripts(codePtr)((codePtr)->nscripts)); | |||
689 | ipagePtr->cacheGen = cacheGen; | |||
690 | } | |||
691 | objsPtr = ipagePtr->cacheObjs; | |||
692 | } | |||
693 | result = AdpExec(itPtr, objc, objv, file, codePtr, objsPtr, outputPtr, &st); | |||
694 | Ns_MutexLock(&servPtr->adp.pagelock); | |||
695 | ++ipagePtr->pagePtr->evals; | |||
696 | if (cachePtr != NULL((void*)0)) { | |||
697 | DecrCache(cachePtr); | |||
698 | } | |||
699 | Ns_MutexUnlock(&servPtr->adp.pagelock); | |||
700 | } | |||
701 | if (itPtr->adp.debugLevel > 0) { | |||
702 | --itPtr->adp.debugLevel; | |||
703 | } | |||
704 | ||||
705 | done: | |||
706 | Ns_DStringFreeTcl_DStringFree(&path); | |||
707 | Ns_DStringFreeTcl_DStringFree(&tmp); | |||
708 | ||||
709 | return result; | |||
710 | } | |||
711 | ||||
712 | ||||
713 | /* | |||
714 | *---------------------------------------------------------------------- | |||
715 | * | |||
716 | * NsAdpDebug -- | |||
717 | * | |||
718 | * Initialize the debugger by calling the debug init proc with | |||
719 | * the hostname and port of the debugger and a pattern of procs | |||
720 | * to auto-instrument. | |||
721 | * | |||
722 | * Results: | |||
723 | * TCL_OK if debugger initialized, TCL_ERROR otherwise. | |||
724 | * | |||
725 | * Side effects: | |||
726 | * Interp is marked for delete on next deallocation. | |||
727 | * | |||
728 | *---------------------------------------------------------------------- | |||
729 | */ | |||
730 | ||||
731 | int | |||
732 | NsAdpDebug(NsInterp *itPtr, const char *host, const char *port, const char *procs) | |||
733 | { | |||
734 | Tcl_Interp *interp; | |||
735 | Tcl_DString ds; | |||
736 | int result; | |||
737 | ||||
738 | NS_NONNULL_ASSERT(itPtr != NULL)((void) (0)); | |||
739 | ||||
740 | interp = itPtr->interp; | |||
741 | result = TCL_OK0; | |||
742 | ||||
743 | if (itPtr->adp.debugInit == 0) { | |||
744 | itPtr->deleteInterp = NS_TRUE1; | |||
745 | Tcl_DStringInit(&ds); | |||
746 | Tcl_DStringAppendElement(&ds, itPtr->servPtr->adp.debuginit); | |||
747 | Tcl_DStringAppendElement(&ds, (procs != NULL((void*)0)) ? procs : NS_EMPTY_STRING); | |||
748 | Tcl_DStringAppendElement(&ds, (host != NULL((void*)0)) ? host : NS_EMPTY_STRING); | |||
749 | Tcl_DStringAppendElement(&ds, (port != NULL((void*)0)) ? port : NS_EMPTY_STRING); | |||
750 | result = Tcl_EvalEx(interp, ds.string, ds.length, 0); | |||
751 | Tcl_DStringFree(&ds); | |||
752 | if (result != TCL_OK0) { | |||
753 | NsAdpLogError(itPtr); | |||
754 | result = TCL_ERROR1; | |||
755 | ||||
756 | } else { | |||
757 | /* | |||
758 | * Link the ADP output buffer result to a global variable | |||
759 | * which can be monitored with a variable watch. | |||
760 | */ | |||
761 | ||||
762 | if (Tcl_LinkVar(interp, "ns_adp_output", | |||
763 | (char *) &itPtr->adp.output.string, | |||
764 | TCL_LINK_STRING4 | TCL_LINK_READ_ONLY0x80) != TCL_OK0) { | |||
765 | NsAdpLogError(itPtr); | |||
766 | } | |||
767 | ||||
768 | itPtr->adp.debugInit = 1; | |||
769 | itPtr->adp.debugLevel = 1; | |||
770 | } | |||
771 | } | |||
772 | return result; | |||
773 | } | |||
774 | ||||
775 | ||||
776 | /* | |||
777 | *---------------------------------------------------------------------- | |||
778 | * | |||
779 | * NsTclAdpStatsObjCmd -- | |||
780 | * | |||
781 | * Implements "ns_adp_stats". This command returns statistics about | |||
782 | * cached ADP pages. | |||
783 | * | |||
784 | * Results: | |||
785 | * Standard Tcl result. | |||
786 | * | |||
787 | * Side effects: | |||
788 | * None. | |||
789 | * | |||
790 | *---------------------------------------------------------------------- | |||
791 | */ | |||
792 | ||||
793 | int | |||
794 | NsTclAdpStatsObjCmd(ClientData clientData, Tcl_Interp *interp, | |||
795 | int UNUSED(objc)UNUSED_objc __attribute__((__unused__)), Tcl_Obj *const* UNUSED(objv)UNUSED_objv __attribute__((__unused__))) | |||
796 | { | |||
797 | const NsInterp *itPtr = clientData; | |||
798 | NsServer *servPtr = itPtr->servPtr; | |||
799 | Ns_DStringTcl_DString ds; | |||
800 | Tcl_HashSearch search; | |||
801 | Tcl_HashEntry *hPtr; | |||
802 | ||||
803 | Ns_DStringInitTcl_DStringInit(&ds); | |||
804 | ||||
805 | Ns_MutexLock(&servPtr->adp.pagelock); | |||
806 | hPtr = Tcl_FirstHashEntry(&servPtr->adp.pages, &search); | |||
807 | while (hPtr != NULL((void*)0)) { | |||
808 | const Page *pagePtr = Tcl_GetHashValue(hPtr)((hPtr)->clientData); | |||
809 | char *file = Tcl_GetHashKey(&servPtr->adp.pages, hPtr)((void *) (((&servPtr->adp.pages)->keyType == (1) || (&servPtr->adp.pages)->keyType == (-1)) ? (hPtr)-> key.oneWordValue : (hPtr)->key.string)); | |||
810 | ||||
811 | Ns_DStringPrintf(&ds, "{%s} " | |||
812 | "{dev %" PRIu64"l" "u" " ino %" PRIu64"l" "u" " mtime %" PRIu64"l" "u" " " | |||
813 | "refcnt %d evals %d size %" PROTd"l" "d"" blocks %d scripts %d} ", | |||
814 | file, | |||
815 | (uint64_t) pagePtr->dev, (uint64_t) pagePtr->ino, (uint64_t) pagePtr->mtime, | |||
816 | pagePtr->refcnt, pagePtr->evals, pagePtr->size, | |||
817 | pagePtr->code.nblocks, pagePtr->code.nscripts); | |||
818 | hPtr = Tcl_NextHashEntry(&search); | |||
819 | } | |||
820 | Ns_MutexUnlock(&servPtr->adp.pagelock); | |||
821 | ||||
822 | Tcl_DStringResult(interp, &ds); | |||
823 | ||||
824 | return TCL_OK0; | |||
825 | } | |||
826 | ||||
827 | ||||
828 | /* | |||
829 | *---------------------------------------------------------------------- | |||
830 | * | |||
831 | * ParseFile -- | |||
832 | * | |||
833 | * Read and parse text from a file. The code is complicated | |||
834 | * somewhat to account for changing files. | |||
835 | * | |||
836 | * Results: | |||
837 | * Pointer to new Page structure or NULL on error. | |||
838 | * | |||
839 | * Side effects: | |||
840 | * Error message will be left in interp on failure. | |||
841 | * | |||
842 | *---------------------------------------------------------------------- | |||
843 | */ | |||
844 | ||||
845 | static Page * | |||
846 | ParseFile(const NsInterp *itPtr, const char *file, struct stat *stPtr, unsigned int flags) | |||
847 | { | |||
848 | Tcl_Interp *interp; | |||
849 | Tcl_Encoding encoding; | |||
850 | Tcl_DString utf; | |||
851 | char *buf = NULL((void*)0); | |||
852 | int fd, tries = 0; | |||
853 | size_t size = 0u; | |||
854 | ssize_t n; | |||
855 | Page *pagePtr = NULL((void*)0); | |||
856 | ||||
857 | NS_NONNULL_ASSERT(itPtr != NULL)((void) (0)); | |||
858 | NS_NONNULL_ASSERT(file != NULL)((void) (0)); | |||
859 | NS_NONNULL_ASSERT(stPtr != NULL)((void) (0)); | |||
860 | ||||
861 | interp = itPtr->interp; | |||
862 | ||||
863 | fd = ns_openopen(file, O_RDONLY00 | O_BINARY(0) | O_CLOEXEC02000000, 0); | |||
864 | if (fd < 0) { | |||
865 | Ns_TclPrintfResult(interp, "could not open \"%s\": %s", | |||
866 | file, Tcl_PosixError(interp)); | |||
867 | return NULL((void*)0); | |||
868 | } | |||
869 | ||||
870 | do { | |||
871 | /* | |||
872 | * fstat() the open file to ensure it has not changed or been replaced | |||
873 | * since the original stat(). | |||
874 | */ | |||
875 | ||||
876 | if (fstat(fd, stPtr) != 0) { | |||
877 | Ns_TclPrintfResult(interp, "could not fstat \"%s\": %s", | |||
878 | file, Tcl_PosixError(interp)); | |||
879 | goto done; | |||
880 | } | |||
881 | size = (size_t)stPtr->st_size; | |||
882 | buf = ns_realloc(buf, size + 1u); | |||
883 | ||||
884 | /* | |||
885 | * Attempt to read +1 byte to catch the file growing. | |||
886 | */ | |||
887 | ||||
888 | n = ns_readread(fd, buf, size + 1u); | |||
889 | if (n < 0) { | |||
890 | Ns_TclPrintfResult(interp, "could not read \"%s\": %s", | |||
891 | file, Tcl_PosixError(interp)); | |||
892 | goto done; | |||
893 | } | |||
894 | if ((size_t)n != size) { | |||
895 | /* | |||
896 | * File is not expected size, rewind and fstat/read again. | |||
897 | */ | |||
898 | ||||
899 | if (ns_lseeklseek(fd, (off_t) 0, SEEK_SET0) != 0) { | |||
900 | Ns_TclPrintfResult(interp, "could not lseek \"%s\": %s", | |||
901 | file, Tcl_PosixError(interp)); | |||
902 | goto done; | |||
903 | } | |||
904 | Ns_ThreadYield(); | |||
905 | } | |||
906 | } while ((size_t)n != size && ++tries < 10); | |||
907 | ||||
908 | if ((size_t)n != size) { | |||
909 | Ns_TclPrintfResult(interp, "inconsistent file: %s", file); | |||
910 | } else { | |||
911 | char *page; | |||
912 | ||||
913 | buf[n] = '\0'; | |||
914 | Tcl_DStringInit(&utf); | |||
915 | encoding = Ns_GetFileEncoding(file); | |||
916 | if (encoding == NULL((void*)0)) { | |||
917 | page = buf; | |||
918 | } else { | |||
919 | page = Tcl_ExternalToUtfDString(encoding, buf, (int)n, &utf); | |||
920 | } | |||
921 | pagePtr = ns_malloc(sizeof(Page)); | |||
922 | pagePtr->servPtr = itPtr->servPtr; | |||
923 | pagePtr->flags = flags; | |||
924 | pagePtr->refcnt = 0; | |||
925 | pagePtr->evals = 0; | |||
926 | pagePtr->locked = NS_FALSE0; | |||
927 | pagePtr->cacheGen = 0; | |||
928 | pagePtr->cachePtr = NULL((void*)0); | |||
929 | pagePtr->mtime = stPtr->st_mtimest_mtim.tv_sec; | |||
930 | pagePtr->size = stPtr->st_size; | |||
931 | pagePtr->dev = stPtr->st_dev; | |||
932 | pagePtr->ino = stPtr->st_ino; | |||
933 | NsAdpParse(&pagePtr->code, itPtr->servPtr, page, flags, file); | |||
934 | Tcl_DStringFree(&utf); | |||
935 | } | |||
936 | ||||
937 | done: | |||
938 | ns_free(buf); | |||
939 | (void) ns_closeclose(fd); | |||
940 | ||||
941 | return pagePtr; | |||
942 | } | |||
943 | ||||
944 | ||||
945 | /* | |||
946 | *---------------------------------------------------------------------- | |||
947 | * | |||
948 | * NsAdpLogError -- | |||
949 | * | |||
950 | * Log an ADP error, possibly invoking the log handling ADP | |||
951 | * file if configured. | |||
952 | * | |||
953 | * Results: | |||
954 | * None. | |||
955 | * | |||
956 | * Side effects: | |||
957 | * Depends on log handler. | |||
958 | * | |||
959 | *---------------------------------------------------------------------- | |||
960 | */ | |||
961 | ||||
962 | void | |||
963 | NsAdpLogError(NsInterp *itPtr) | |||
964 | { | |||
965 | Tcl_Interp *interp; | |||
966 | const Ns_Conn *conn; | |||
967 | Ns_DStringTcl_DString ds; | |||
968 | const AdpFrame *framePtr; | |||
969 | int len; | |||
970 | const char *err, *adp, *inc, *dot; | |||
971 | ||||
972 | NS_NONNULL_ASSERT(itPtr != NULL)((void) (0)); | |||
973 | ||||
974 | interp = itPtr->interp; | |||
975 | conn = itPtr->conn; | |||
976 | ||||
977 | framePtr = itPtr->adp.framePtr; | |||
978 | Ns_DStringInitTcl_DStringInit(&ds); | |||
979 | ||||
980 | if (framePtr != NULL((void*)0)) { | |||
981 | Ns_DStringPrintf(&ds, "\n at line %d of ", | |||
982 | (int)framePtr->line + Tcl_GetErrorLine(interp)); | |||
983 | } | |||
984 | inc = NS_EMPTY_STRING; | |||
985 | while (framePtr != NULL((void*)0)) { | |||
986 | if (framePtr->file != NULL((void*)0)) { | |||
987 | Ns_DStringPrintf(&ds, "%sadp file \"%s\"", inc, framePtr->file); | |||
988 | if (framePtr->ident != NULL((void*)0)) { | |||
989 | Ns_DStringPrintf(&ds, " {%s}", Tcl_GetString(framePtr->ident)); | |||
990 | } | |||
991 | } else { | |||
992 | adp = Tcl_GetStringFromObj(framePtr->objv[0], &len); | |||
993 | dot = NS_EMPTY_STRING; | |||
994 | if (len > 150) { | |||
995 | len = 150; | |||
996 | dot = "..."; | |||
997 | } | |||
998 | while (((unsigned char)adp[len] & 0xC0u) == 0x80u) { | |||
999 | /* | |||
1000 | * Avoid truncating multi-byte UTF-8 character. | |||
1001 | */ | |||
1002 | len--; | |||
1003 | dot = "..."; | |||
1004 | } | |||
1005 | Ns_DStringPrintf(&ds, "%sadp script:\n\"%.*s%s\"", | |||
1006 | inc, len, adp, dot); | |||
1007 | } | |||
1008 | framePtr = framePtr->prevPtr; | |||
1009 | inc = "\n included from "; | |||
1010 | } | |||
1011 | if (conn != NULL((void*)0) && (itPtr->adp.flags & ADP_DETAIL0x80u) != 0u) { | |||
1012 | size_t i; | |||
1013 | ||||
1014 | Ns_DStringPrintf(&ds, "\n while processing connection %s:\n%8s%s", | |||
1015 | NsConnIdStr(conn), NS_EMPTY_STRING, | |||
1016 | conn->request.line); | |||
1017 | for (i = 0u; i < Ns_SetSize(conn->headers)((conn->headers)->size); ++i) { | |||
1018 | Ns_DStringPrintf(&ds, "\n %s: %s", | |||
1019 | Ns_SetKey(conn->headers, i)((conn->headers)->fields[(i)].name), | |||
1020 | Ns_SetValue(conn->headers, i)((conn->headers)->fields[(i)].value)); | |||
1021 | } | |||
1022 | } | |||
1023 | err = Ns_TclLogErrorInfo(interp, ds.string); | |||
1024 | if ((itPtr->adp.flags & ADP_DISPLAY0x200u) != 0u) { | |||
1025 | Ns_DStringSetLengthTcl_DStringSetLength(&ds, 0); | |||
1026 | Ns_DStringAppend(&ds, "<br><pre>\n")Tcl_DStringAppend((&ds), ("<br><pre>\n"), -1); | |||
1027 | Ns_QuoteHtml(&ds, err); | |||
1028 | Ns_DStringAppend(&ds, "\n<br></pre>\n")Tcl_DStringAppend((&ds), ("\n<br></pre>\n"), - 1); | |||
1029 | (void)NsAdpAppend(itPtr, ds.string, ds.length); | |||
1030 | } | |||
1031 | Ns_DStringFreeTcl_DStringFree(&ds); | |||
1032 | adp = itPtr->servPtr->adp.errorpage; | |||
1033 | if (adp != NULL((void*)0) && itPtr->adp.errorLevel == 0) { | |||
1034 | Tcl_Obj *objv[2]; | |||
1035 | ||||
1036 | ++itPtr->adp.errorLevel; | |||
1037 | objv[0] = Tcl_NewStringObj(adp, -1); | |||
1038 | Tcl_IncrRefCount(objv[0])++(objv[0])->refCount; | |||
1039 | objv[1] = Tcl_GetVar2Ex(interp, "errorInfo", NULL((void*)0), TCL_GLOBAL_ONLY1); | |||
1040 | if (objv[1] == NULL((void*)0)) { | |||
1041 | objv[1] = Tcl_GetObjResult(interp); | |||
1042 | } | |||
1043 | (void) NsAdpInclude(itPtr, 2, objv, adp, NULL((void*)0)); | |||
1044 | Tcl_DecrRefCount(objv[0])do { Tcl_Obj *_objPtr = (objv[0]); if (_objPtr->refCount-- <= 1) { TclFreeObj(_objPtr); } } while(0); | |||
1045 | --itPtr->adp.errorLevel; | |||
1046 | } | |||
1047 | } | |||
1048 | ||||
1049 | ||||
1050 | /* | |||
1051 | *---------------------------------------------------------------------- | |||
1052 | * | |||
1053 | * AdpExec -- | |||
1054 | * | |||
1055 | * Execute ADP code. | |||
1056 | * | |||
1057 | * Results: | |||
1058 | * TCL_OK unless there is an ADP error exception, stack overflow, | |||
1059 | * or script error when the ADP_STRICT option is set. | |||
1060 | * | |||
1061 | * Side Effects: | |||
1062 | * Depends on page. | |||
1063 | * | |||
1064 | *---------------------------------------------------------------------- | |||
1065 | */ | |||
1066 | ||||
1067 | static int | |||
1068 | AdpExec(NsInterp *itPtr, int objc, Tcl_Obj *const* objv, const char *file, | |||
1069 | const AdpCode *codePtr, Objs *objsPtr, Tcl_DString *outputPtr, | |||
1070 | const struct stat *stPtr) | |||
1071 | { | |||
1072 | Tcl_Interp *interp; | |||
1073 | AdpFrame frame; | |||
1074 | Ns_DStringTcl_DString cwd; | |||
1075 | Tcl_Obj *objPtr; | |||
1076 | int nscript, nblocks, result, i; | |||
1077 | const char *ptr, *savecwd; | |||
1078 | ||||
1079 | NS_NONNULL_ASSERT(itPtr != NULL)((void) (0)); | |||
1080 | NS_NONNULL_ASSERT(codePtr != NULL)((void) (0)); | |||
1081 | NS_NONNULL_ASSERT(outputPtr != NULL)((void) (0)); | |||
1082 | ||||
1083 | interp = itPtr->interp; | |||
1084 | ||||
1085 | /* | |||
1086 | * Setup the new call frame. | |||
1087 | */ | |||
1088 | ||||
1089 | Ns_DStringInitTcl_DStringInit(&cwd); | |||
1090 | frame.file = file; | |||
1091 | frame.objc = (unsigned short)objc; | |||
1092 | frame.objv = (Tcl_Obj **)objv; | |||
1093 | if (stPtr != NULL((void*)0)) { | |||
1094 | frame.size = stPtr->st_size; | |||
1095 | frame.mtime = stPtr->st_mtimest_mtim.tv_sec; | |||
1096 | } else { | |||
1097 | frame.size = 0; | |||
1098 | frame.mtime = 0; | |||
1099 | } | |||
1100 | frame.outputPtr = outputPtr; | |||
1101 | frame.ident = NULL((void*)0); | |||
1102 | savecwd = itPtr->adp.cwd; | |||
1103 | if (file != NULL((void*)0)) { | |||
1104 | const char *slash = strrchr(file, INTCHAR('/')((int)((unsigned char)(('/'))))); | |||
1105 | if (slash != NULL((void*)0)) { | |||
1106 | Ns_DStringNAppendTcl_DStringAppend(&cwd, file, (int)(slash - file)); | |||
1107 | itPtr->adp.cwd = cwd.string; | |||
1108 | } | |||
1109 | } | |||
1110 | frame.prevPtr = itPtr->adp.framePtr; | |||
1111 | itPtr->adp.framePtr = &frame; | |||
1112 | itPtr->adp.depth++; | |||
1113 | ||||
1114 | /* | |||
1115 | * Execute the ADP by copying text blocks directly to the output | |||
1116 | * stream and evaluating script blocks. | |||
1117 | */ | |||
1118 | ||||
1119 | ptr = AdpCodeText(codePtr)((codePtr)->text.string); | |||
1120 | nblocks = AdpCodeBlocks(codePtr)((codePtr)->nblocks); | |||
1121 | nscript = 0; | |||
1122 | result = TCL_OK0; | |||
1123 | for (i = 0; itPtr->adp.exception == ADP_OK && i < nblocks; ++i) { | |||
1124 | int len; | |||
1125 | ||||
1126 | frame.line = (unsigned short)AdpCodeLine(codePtr, i)((codePtr)->line[(i)]); | |||
1127 | len = AdpCodeLen(codePtr, i)((codePtr)->len[(i)]); | |||
1128 | if ((itPtr->adp.flags & ADP_TRACE0x20u) != 0u) { | |||
1129 | AdpTrace(itPtr, ptr, len); | |||
1130 | } | |||
1131 | if (len > 0) { | |||
1132 | result = NsAdpAppend(itPtr, ptr, len); | |||
1133 | } else { | |||
1134 | len = -len; | |||
1135 | if (itPtr->adp.debugLevel > 0) { | |||
1136 | result = AdpDebug(itPtr, ptr, len, nscript); | |||
1137 | } else if (objsPtr == NULL((void*)0)) { | |||
1138 | result = Tcl_EvalEx(interp, ptr, len, 0); | |||
1139 | } else { | |||
1140 | assert(nscript < objsPtr->nobjs)((void) (0)); | |||
1141 | objPtr = objsPtr->objs[nscript]; | |||
1142 | if (objPtr == NULL((void*)0)) { | |||
1143 | objPtr = Tcl_NewStringObj(ptr, len); | |||
1144 | Tcl_IncrRefCount(objPtr)++(objPtr)->refCount; | |||
1145 | objsPtr->objs[nscript] = objPtr; | |||
1146 | } | |||
1147 | result = Tcl_EvalObjEx(interp, objPtr, 0); | |||
1148 | } | |||
1149 | ++nscript; | |||
1150 | ||||
1151 | /* | |||
1152 | * Propagate NS_TIMEOUT errors from Tcl code. | |||
1153 | */ | |||
1154 | ||||
1155 | if (result == TCL_ERROR1) { | |||
1156 | if (NsTclTimeoutException(interp) == NS_TRUE1) { | |||
1157 | itPtr->adp.exception = ADP_TIMEOUT; | |||
1158 | } | |||
1159 | } | |||
1160 | } | |||
1161 | ||||
1162 | /* | |||
1163 | * Log an error message and optionally break from this ADP | |||
1164 | * call frame unless the error was generated to signal | |||
1165 | * and ADP exception. | |||
1166 | */ | |||
1167 | ||||
1168 | if (result != TCL_OK0 && itPtr->adp.exception == ADP_OK) { | |||
1169 | if ((itPtr->adp.flags & ADP_ERRLOGGED0x1000u) == 0u) { | |||
1170 | NsAdpLogError(itPtr); | |||
1171 | } | |||
1172 | if ((itPtr->adp.flags & ADP_STRICT0x100u) != 0u) { | |||
1173 | itPtr->adp.flags |= ADP_ERRLOGGED0x1000u; | |||
1174 | break; | |||
1175 | } | |||
1176 | } | |||
1177 | ptr += len; | |||
1178 | } | |||
1179 | ||||
1180 | /* | |||
1181 | * Clear the return exception and reset result. | |||
1182 | */ | |||
1183 | ||||
1184 | switch (itPtr->adp.exception) { | |||
1185 | case ADP_OK: | |||
1186 | break; | |||
1187 | case ADP_RETURN: | |||
1188 | itPtr->adp.exception = ADP_OK; | |||
1189 | NS_FALL_THROUGH((void)0); /* fall through */ | |||
1190 | case ADP_ABORT: | |||
1191 | NS_FALL_THROUGH((void)0); /* fall through */ | |||
1192 | case ADP_BREAK: | |||
1193 | NS_FALL_THROUGH((void)0); /* fall through */ | |||
1194 | case ADP_TIMEOUT: | |||
1195 | result = TCL_OK0; | |||
1196 | break; | |||
1197 | } | |||
1198 | ||||
1199 | /* | |||
1200 | * Restore the previous call frame. | |||
1201 | */ | |||
1202 | ||||
1203 | itPtr->adp.framePtr = frame.prevPtr; | |||
1204 | itPtr->adp.cwd = savecwd; | |||
1205 | if (frame.ident != NULL((void*)0)) { | |||
1206 | Tcl_DecrRefCount(frame.ident)do { Tcl_Obj *_objPtr = (frame.ident); if (_objPtr->refCount -- <= 1) { TclFreeObj(_objPtr); } } while(0); | |||
1207 | } | |||
1208 | Ns_DStringFreeTcl_DStringFree(&cwd); | |||
1209 | ||||
1210 | return result; | |||
1211 | } | |||
1212 | ||||
1213 | ||||
1214 | /* | |||
1215 | *---------------------------------------------------------------------- | |||
1216 | * | |||
1217 | * AdpDebug -- | |||
1218 | * | |||
1219 | * Evaluate an ADP script block with the TclPro debugger. | |||
1220 | * | |||
1221 | * Results: | |||
1222 | * Depends on script. | |||
1223 | * | |||
1224 | * Side effects: | |||
1225 | * A unique temp file with header comments and the script is | |||
1226 | * created and sourced, the effect of which is TclPro will | |||
1227 | * instrument the code on the fly for single-step debugging. | |||
1228 | * | |||
1229 | *---------------------------------------------------------------------- | |||
1230 | */ | |||
1231 | ||||
1232 | static int | |||
1233 | AdpDebug(const NsInterp *itPtr, const char *ptr, int len, int nscript) | |||
1234 | { | |||
1235 | Tcl_Interp *interp; | |||
1236 | int level; | |||
1237 | const char *file; | |||
1238 | char debugfile[255]; | |||
1239 | Ns_DStringTcl_DString ds; | |||
1240 | int result, fd; | |||
1241 | ||||
1242 | NS_NONNULL_ASSERT(itPtr != NULL)((void) (0)); | |||
1243 | NS_NONNULL_ASSERT(ptr != NULL)((void) (0)); | |||
1244 | ||||
1245 | interp = itPtr->interp; | |||
1246 | level = itPtr->adp.debugLevel; | |||
1247 | file = Tcl_GetString(itPtr->adp.framePtr->objv[0]); | |||
1248 | ||||
1249 | Ns_DStringInitTcl_DStringInit(&ds); | |||
1250 | Ns_DStringPrintf(&ds, "#\n" | |||
1251 | "# level: %d\n" | |||
1252 | "# chunk: %d\n" | |||
1253 | "# file: %s\n" | |||
1254 | "#\n\n", level, nscript, file); | |||
1255 | Ns_DStringNAppendTcl_DStringAppend(&ds, ptr, len); | |||
1256 | ||||
1257 | snprintf(debugfile, sizeof(debugfile),__builtin___snprintf_chk (debugfile, sizeof(debugfile), 2 - 1 , __builtin_object_size (debugfile, 2 > 1), "/tmp" "/adp%d.%d.XXXXXX" , level, nscript) | |||
1258 | P_tmpdir "/adp%d.%d.XXXXXX",__builtin___snprintf_chk (debugfile, sizeof(debugfile), 2 - 1 , __builtin_object_size (debugfile, 2 > 1), "/tmp" "/adp%d.%d.XXXXXX" , level, nscript) | |||
1259 | level, nscript)__builtin___snprintf_chk (debugfile, sizeof(debugfile), 2 - 1 , __builtin_object_size (debugfile, 2 > 1), "/tmp" "/adp%d.%d.XXXXXX" , level, nscript); | |||
1260 | fd = ns_mkstempmkstemp(debugfile); | |||
1261 | if (fd < 0) { | |||
1262 | Ns_TclPrintfResult(interp, "could not create ADP debug file"); | |||
1263 | result = TCL_ERROR1; | |||
1264 | } else { | |||
1265 | if (ns_writewrite(fd, ds.string, (size_t)ds.length) < 0) { | |||
1266 | Ns_TclPrintfResult(interp, "write to \"%s\" failed: %s", | |||
1267 | debugfile, Tcl_PosixError(interp)); | |||
1268 | result = TCL_ERROR1; | |||
1269 | } else { | |||
1270 | Ns_DStringSetLengthTcl_DStringSetLength(&ds, 0); | |||
1271 | Ns_DStringVarAppend(&ds, "source ", debugfile, (char *)0L); | |||
1272 | result = Tcl_EvalEx(interp, ds.string, ds.length, 0); | |||
1273 | } | |||
1274 | (void) ns_closeclose(fd); | |||
1275 | unlink(debugfile); | |||
1276 | } | |||
1277 | Ns_DStringFreeTcl_DStringFree(&ds); | |||
1278 | ||||
1279 | return result; | |||
1280 | } | |||
1281 | ||||
1282 | ||||
1283 | /* | |||
1284 | *---------------------------------------------------------------------- | |||
1285 | * | |||
1286 | * FreeInterpPage -- | |||
1287 | * | |||
1288 | * Free a per-interp page cache entry. | |||
1289 | * | |||
1290 | * Results: | |||
1291 | * None. | |||
1292 | * | |||
1293 | * Side Effects: | |||
1294 | * None. | |||
1295 | * | |||
1296 | *---------------------------------------------------------------------- | |||
1297 | */ | |||
1298 | ||||
1299 | static void | |||
1300 | FreeInterpPage(void *arg) | |||
1301 | { | |||
1302 | InterpPage *ipagePtr = arg; | |||
1303 | Page *pagePtr = ipagePtr->pagePtr; | |||
1304 | NsServer *servPtr = pagePtr->servPtr; | |||
1305 | ||||
1306 | FreeObjs(ipagePtr->objs); | |||
| ||||
1307 | Ns_MutexLock(&servPtr->adp.pagelock); | |||
1308 | if (--pagePtr->refcnt == 0) { | |||
1309 | if (pagePtr->hPtr != NULL((void*)0)) { | |||
1310 | Tcl_DeleteHashEntry(pagePtr->hPtr); | |||
1311 | } | |||
1312 | if (pagePtr->cachePtr != NULL((void*)0)) { | |||
1313 | FreeObjs(ipagePtr->cacheObjs); | |||
1314 | DecrCache(pagePtr->cachePtr); | |||
1315 | } | |||
1316 | NsAdpFreeCode(&pagePtr->code); | |||
1317 | ns_free(pagePtr); | |||
1318 | } | |||
1319 | Ns_MutexUnlock(&servPtr->adp.pagelock); | |||
1320 | ns_free(ipagePtr); | |||
1321 | } | |||
1322 | ||||
1323 | ||||
1324 | /* | |||
1325 | *---------------------------------------------------------------------- | |||
1326 | * | |||
1327 | * AllocObjs -- | |||
1328 | * | |||
1329 | * Allocate new page script objects. | |||
1330 | * | |||
1331 | * Results: | |||
1332 | * Pointer to new objects. | |||
1333 | * | |||
1334 | * Side Effects: | |||
1335 | * None. | |||
1336 | * | |||
1337 | *---------------------------------------------------------------------- | |||
1338 | */ | |||
1339 | ||||
1340 | static Objs * | |||
1341 | AllocObjs(int nobjs) | |||
1342 | { | |||
1343 | Objs *objsPtr; | |||
1344 | ||||
1345 | objsPtr = ns_calloc(1u, sizeof(Objs) + ((size_t)nobjs * sizeof(Tcl_Obj *))); | |||
1346 | objsPtr->nobjs = nobjs; | |||
1347 | ||||
1348 | return objsPtr; | |||
1349 | } | |||
1350 | ||||
1351 | ||||
1352 | /* | |||
1353 | *---------------------------------------------------------------------- | |||
1354 | * | |||
1355 | * FreeObjs -- | |||
1356 | * | |||
1357 | * Free page objects, decrementing ref counts as needed. | |||
1358 | * | |||
1359 | * Results: | |||
1360 | * None. | |||
1361 | * | |||
1362 | * Side Effects: | |||
1363 | * None. | |||
1364 | * | |||
1365 | *---------------------------------------------------------------------- | |||
1366 | */ | |||
1367 | ||||
1368 | static void | |||
1369 | FreeObjs(Objs *objsPtr) | |||
1370 | { | |||
1371 | int i; | |||
1372 | ||||
1373 | NS_NONNULL_ASSERT(objsPtr != NULL)((void) (0)); | |||
1374 | ||||
1375 | for (i = 0; i < objsPtr->nobjs; ++i) { | |||
1376 | if (objsPtr->objs[i] != NULL((void*)0)) { | |||
| ||||
1377 | Tcl_DecrRefCount(objsPtr->objs[i])do { Tcl_Obj *_objPtr = (objsPtr->objs[i]); if (_objPtr-> refCount-- <= 1) { TclFreeObj(_objPtr); } } while(0); | |||
1378 | } | |||
1379 | } | |||
1380 | ns_free(objsPtr); | |||
1381 | } | |||
1382 | ||||
1383 | ||||
1384 | /* | |||
1385 | *---------------------------------------------------------------------- | |||
1386 | * | |||
1387 | * DecrCache -- | |||
1388 | * | |||
1389 | * Decrement ref count of a cache entry, potentially freeing | |||
1390 | * the cache. | |||
1391 | * | |||
1392 | * Results: | |||
1393 | * None. | |||
1394 | * | |||
1395 | * Side Effects: | |||
1396 | * Will free cache on last reference count. | |||
1397 | * | |||
1398 | *---------------------------------------------------------------------- | |||
1399 | */ | |||
1400 | ||||
1401 | static void | |||
1402 | DecrCache(AdpCache *cachePtr) | |||
1403 | { | |||
1404 | NS_NONNULL_ASSERT(cachePtr != NULL)((void) (0)); | |||
1405 | ||||
1406 | if (--cachePtr->refcnt == 0) { | |||
1407 | NsAdpFreeCode(&cachePtr->code); | |||
1408 | ns_free(cachePtr); | |||
1409 | } | |||
1410 | } | |||
1411 | ||||
1412 | ||||
1413 | /* | |||
1414 | *---------------------------------------------------------------------- | |||
1415 | * | |||
1416 | * AdpTrace -- | |||
1417 | * | |||
1418 | * Trace execution of an ADP page. | |||
1419 | * | |||
1420 | * Results: | |||
1421 | * None. | |||
1422 | * | |||
1423 | * Side effects: | |||
1424 | * Dumps tracing info, possibly truncated, via Ns_Log. | |||
1425 | * | |||
1426 | *---------------------------------------------------------------------- | |||
1427 | */ | |||
1428 | ||||
1429 | static void | |||
1430 | AdpTrace(const NsInterp *itPtr, const char *ptr, int len) | |||
1431 | { | |||
1432 | char type; | |||
1433 | ||||
1434 | NS_NONNULL_ASSERT(itPtr != NULL)((void) (0)); | |||
1435 | NS_NONNULL_ASSERT(ptr != NULL)((void) (0)); | |||
1436 | ||||
1437 | if (len >= 0) { | |||
1438 | type = 'T'; | |||
1439 | } else { | |||
1440 | type = 'S'; | |||
1441 | len = -len; | |||
1442 | } | |||
1443 | if (len > itPtr->servPtr->adp.tracesize) { | |||
1444 | len = itPtr->servPtr->adp.tracesize; | |||
1445 | } | |||
1446 | Ns_Log(Notice, "adp[%d%c]: %.*s", itPtr->adp.depth, type, len, ptr); | |||
1447 | } | |||
1448 | ||||
1449 | /* | |||
1450 | * Local Variables: | |||
1451 | * mode: c | |||
1452 | * c-basic-offset: 4 | |||
1453 | * fill-column: 78 | |||
1454 | * indent-tabs-mode: nil | |||
1455 | * End: | |||
1456 | */ |