Bug Summary

File:d/tclinit.c
Warning:line 2363, column 9
Duplicate code detected
Note:line 2382, column 9
Similar code here

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name tclinit.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/home/isvv/naviserver/nsd -resource-dir /usr/local/lib/clang/15.0.0 -D _FORTIFY_SOURCE=2 -D NDEBUG -D SYSTEM_MALLOC -I ../include -I /usr/include/tcl8.6 -D HAVE_CONFIG_H -internal-isystem /usr/local/lib/clang/15.0.0/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/11/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -std=c99 -fdebug-compilation-dir=/home/isvv/naviserver/nsd -ferror-limit 19 -stack-protector 2 -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -analyzer-checker alpha -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2022-07-23-130959-11103-1 -x c tclinit.c
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 * tclinit.c --
32 *
33 * Initialization and resource management routines for Tcl.
34 */
35
36#include "nsd.h"
37
38/*
39 * The following structure maintains interp trace callbacks.
40 */
41
42typedef struct TclTrace {
43 struct TclTrace *nextPtr;
44 struct TclTrace *prevPtr;
45 Ns_TclTraceProc *proc;
46 const void *arg;
47 Ns_TclTraceType when;
48} TclTrace;
49
50/*
51 * The following structure maintains procs to call during interp garbage
52 * collection. Unlike traces, these callbacks are one-shot events
53 * registered during normal Tcl script evaluation. The callbacks are
54 * invoked in FIFO order (LIFO would probably have been better). In
55 * practice this API is rarely used. Instead, more specific garbage
56 * collection schemes are used; see the "ns_cleanup" script in init.tcl
57 * for examples.
58 */
59
60typedef struct Defer {
61 struct Defer *nextPtr;
62 Ns_TclDeferProc *proc;
63 void *arg;
64} Defer;
65
66/*
67 * The following structure maintains scripts to execute when the
68 * connection is closed. The scripts are invoked in LIFO order.
69 */
70
71typedef struct AtClose {
72 struct AtClose *nextPtr;
73 Tcl_Obj *objPtr;
74} AtClose;
75
76static Ns_ObjvTable traceWhen[] = {
77 {"allocate", (unsigned int)NS_TCL_TRACE_ALLOCATE},
78 {"create", (unsigned int)NS_TCL_TRACE_CREATE},
79 {"deallocate", (unsigned int)NS_TCL_TRACE_DEALLOCATE},
80 {"delete", (unsigned int)NS_TCL_TRACE_DELETE},
81 {"freeconn", (unsigned int)NS_TCL_TRACE_FREECONN},
82 {"getconn", (unsigned int)NS_TCL_TRACE_GETCONN},
83 {"idle", (unsigned int)NS_TCL_TRACE_IDLE},
84 {NULL((void*)0), (unsigned int)0}
85};
86
87
88/*
89 * Static functions defined in this file.
90 */
91
92static NsInterp *PopInterp(NsServer *servPtr, Tcl_Interp *interp)
93 NS_GNUC_RETURNS_NONNULL;
94
95static void PushInterp(NsInterp *itPtr)
96 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
97
98static Tcl_HashEntry *GetCacheEntry(const NsServer *servPtr)
99 NS_GNUC_RETURNS_NONNULL;
100
101static Tcl_Interp *CreateInterp(NsInterp **itPtrPtr, NsServer *servPtr)
102 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)))
103 NS_GNUC_RETURNS_NONNULL;
104
105static NsInterp *NewInterpData(Tcl_Interp *interp, NsServer *servPtr)
106 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
107
108static int UpdateInterp(NsInterp *itPtr)
109 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
110
111static void RunTraces(NsInterp *itPtr, Ns_TclTraceType why)
112 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
113
114static void LogTrace(const NsInterp *itPtr, const TclTrace *tracePtr, Ns_TclTraceType why)
115 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2)));
116
117static void LogErrorInTrace(const NsInterp *itPtr, const char *context, Ns_TclTraceType why)
118 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
119
120static Ns_ReturnCode RegisterAt(Ns_TclTraceProc *proc, const void *arg, Ns_TclTraceType when)
121 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
122
123static const char *GetTraceLabel(unsigned int traceWhy);
124
125static Tcl_InterpDeleteProc FreeInterpData;
126static Ns_TlsCleanup DeleteInterps;
127static Ns_ServerInitProc ConfigServerTcl;
128
129static int ICtlAddTrace(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv, Ns_TclTraceType when);
130
131static Tcl_ObjCmdProc ICtlAddModuleObjCmd;
132static Tcl_ObjCmdProc ICtlCleanupObjCmd;
133static Tcl_ObjCmdProc ICtlEpochObjCmd;
134static Tcl_ObjCmdProc ICtlGetModulesObjCmd;
135static Tcl_ObjCmdProc ICtlGetObjCmd;
136static Tcl_ObjCmdProc ICtlGetTracesObjCmd;
137static Tcl_ObjCmdProc ICtlMarkForDeleteObjCmd;
138static Tcl_ObjCmdProc ICtlOnCleanupObjCmd;
139static Tcl_ObjCmdProc ICtlOnCreateObjCmd;
140static Tcl_ObjCmdProc ICtlOnDeleteObjCmd;
141static Tcl_ObjCmdProc ICtlRunTracesObjCmd;
142static Tcl_ObjCmdProc ICtlSaveObjCmd;
143static Tcl_ObjCmdProc ICtlTraceObjCmd;
144static Tcl_ObjCmdProc ICtlUpdateObjCmd;
145
146/*
147 * Static variables defined in this file.
148 */
149
150static Ns_Tls tls; /* Slot for per-thread Tcl interp cache. */
151
152static Ns_Mutex updateLock = NULL((void*)0);
153static int concurrentUpdates = 0;
154static int maxConcurrentUpdates = 1000;
155
156static Ns_Mutex interpLock = NULL((void*)0);
157static bool_Bool concurrent_interp_create = NS_FALSE0;
158
159static const char *
160GetTraceLabel(unsigned int traceWhy) {
161 unsigned int i = 0u;
162 const char *result = "none";
163
164 while (traceWhen[i].key != NULL((void*)0)) {
165 if (traceWhen[i].value == traceWhy) {
166 result = traceWhen[i].key;
167 break;
168 }
169 i++;
170 }
171 return result;
172}
173
174
175/*
176 *----------------------------------------------------------------------
177 *
178 * Nsd_Init --
179 *
180 * Init routine called when libnsd is loaded via the Tcl
181 * load command.
182 *
183 * Results:
184 * Always TCL_OK.
185 *
186 * Side effects:
187 * See Ns_TclInit.
188 *
189 *----------------------------------------------------------------------
190 */
191
192int
193Nsd_Init(Tcl_Interp *interp)
194{
195 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
196
197 return Ns_TclInit(interp);
198}
199
200
201/*
202 *----------------------------------------------------------------------
203 *
204 * NsConfigTcl --
205 *
206 * Allow configuration of Tcl-specific parameters via the configuration file.
207 *
208 * Results:
209 * None.
210 *
211 * Side effects:
212 * Setting static configuration variable.
213 *
214 *----------------------------------------------------------------------
215 */
216
217void
218NsConfigTcl(void)
219{
220 concurrent_interp_create = Ns_ConfigBool(NS_GLOBAL_CONFIG_PARAMETERS"ns/parameters", "concurrentinterpcreate",
221#ifdef NS_TCL_PRE86
222 NS_FALSE0
223#else
224 NS_TRUE1
225#endif
226 );
227 maxConcurrentUpdates = Ns_ConfigIntRange(NS_GLOBAL_CONFIG_PARAMETERS"ns/parameters", "maxconcurrentupdates", 1000, 1, INT_MAX2147483647);
228}
229
230
231/*
232 *----------------------------------------------------------------------
233 *
234 * NsInitTcl --
235 *
236 * Initialize the Tcl interp interface.
237 *
238 * Results:
239 * None.
240 *
241 * Side effects:
242 * None.
243 *
244 *----------------------------------------------------------------------
245 */
246
247void
248NsInitTcl(void)
249{
250 Ns_MutexInit(&interpLock);
251 Ns_MutexSetName(&interpLock, "interp");
252
253 Ns_MutexInit(&updateLock);
254 Ns_MutexSetName(&updateLock, "update");
255 /*
256 * Allocate the thread storage slot for the table of interps
257 * per-thread. At thread exit, DeleteInterps will be called
258 * to free any interps remaining on the thread cache.
259 */
260
261 Ns_TlsAlloc(&tls, DeleteInterps);
262
263 NsRegisterServerInit(ConfigServerTcl);
264}
265
266static Ns_ReturnCode
267ConfigServerTcl(const char *server)
268{
269 NsServer *servPtr;
270 Ns_ReturnCode result;
271
272 NS_NONNULL_ASSERT(server != NULL)((void) (0));
273
274 servPtr = NsGetServer(server);
275
276 if (unlikely(servPtr == NULL)(__builtin_expect((servPtr == ((void*)0)), 0))) {
277 Ns_Log(Warning, "Could configure Tcl; server '%s' unknown", server);
278 result = NS_ERROR;
279
280 } else {
281 Ns_DStringTcl_DString ds;
282 const char *path, *p, *initFileString;
283 int n;
284 Ns_Set *set = NULL((void*)0);
285 bool_Bool initFileStringCopied = NS_FALSE0;
286
287 Ns_ThreadSetName("-main:%s-", server);
288
289 path = Ns_ConfigSectionPath(&set, server, NULL((void*)0), "tcl", (char *)0L);
290
291 Ns_DStringInitTcl_DStringInit(&ds);
292
293 servPtr->tcl.library = Ns_ConfigString(path, "library", "modules/tcl");
294 if (Ns_PathIsAbsolute(servPtr->tcl.library) == NS_FALSE0) {
295 Ns_HomePath(&ds, servPtr->tcl.library, (char *)0L);
296 n = ds.length;
297 servPtr->tcl.library = Ns_DStringExport(&ds);
298 Ns_SetUpdateSz(set, "library", 7, servPtr->tcl.library, n);
299 }
300
301 initFileString = Ns_ConfigString(path, "initfile", "bin/init.tcl");
302 if (Ns_PathIsAbsolute(initFileString) == NS_FALSE0) {
303 Ns_HomePath(&ds, initFileString, (char *)0L);
304 initFileString = Ns_DStringExport(&ds);
305 n = ds.length;
306 Ns_SetUpdateSz(set, "initfile", 8, initFileString, n);
307 initFileStringCopied = NS_TRUE1;
308 }
309 servPtr->tcl.initfile = Tcl_NewStringObj(initFileString, -1);
310 if (initFileStringCopied) {
311 ns_free((char *)initFileString);
312 }
313 Tcl_IncrRefCount(servPtr->tcl.initfile)++(servPtr->tcl.initfile)->refCount;
314
315 servPtr->tcl.modules = Tcl_NewObj();
316 Tcl_IncrRefCount(servPtr->tcl.modules)++(servPtr->tcl.modules)->refCount;
317
318 Ns_RWLockInit(&servPtr->tcl.lock);
319 Ns_RWLockSetName2(&servPtr->tcl.lock, "rw:tcl", server);
320
321 Ns_RWLockInit(&servPtr->tcl.cachelock);
322 Ns_RWLockSetName2(&servPtr->tcl.cachelock, "ns:tcl.cache", server);
323
324 Tcl_InitHashTable(&servPtr->tcl.caches, TCL_STRING_KEYS(0));
325 Tcl_InitHashTable(&servPtr->tcl.runTable, TCL_STRING_KEYS(0));
326 Tcl_InitHashTable(&servPtr->tcl.synch.mutexTable, TCL_STRING_KEYS(0));
327 Tcl_InitHashTable(&servPtr->tcl.synch.csTable, TCL_STRING_KEYS(0));
328 Tcl_InitHashTable(&servPtr->tcl.synch.semaTable, TCL_STRING_KEYS(0));
329 Tcl_InitHashTable(&servPtr->tcl.synch.condTable, TCL_STRING_KEYS(0));
330 Tcl_InitHashTable(&servPtr->tcl.synch.rwTable, TCL_STRING_KEYS(0));
331
332 servPtr->nsv.rwlocks = Ns_ConfigBool(path, "nsvrwlocks", NS_TRUE1);
333 servPtr->nsv.nbuckets = Ns_ConfigIntRange(path, "nsvbuckets", 8, 1, INT_MAX2147483647);
334 servPtr->nsv.buckets = NsTclCreateBuckets(servPtr, servPtr->nsv.nbuckets);
335
336 /*
337 * Initialize the list of connection headers to log for Tcl errors.
338 */
339
340 p = Ns_ConfigGetValue(path, "errorlogheaders");
341 if (p != NULL((void*)0)
342 && Tcl_SplitList(NULL((void*)0), p, &n, &servPtr->tcl.errorLogHeaders) != TCL_OK0) {
343 Ns_Log(Error, "config: errorlogheaders is not a list: %s", p);
344 }
345
346 /*
347 * Initialize the Tcl detached channel support.
348 */
349
350 Tcl_InitHashTable(&servPtr->chans.table, TCL_STRING_KEYS(0));
351 Ns_MutexInit(&servPtr->chans.lock);
352 Ns_MutexSetName2(&servPtr->chans.lock, "nstcl:chans", server);
353
354 Tcl_InitHashTable(&servPtr->connchans.table, TCL_STRING_KEYS(0));
355 Ns_RWLockInit(&servPtr->connchans.lock);
356 Ns_RWLockSetName2(&servPtr->connchans.lock, "nstcl:connchans", server);
357 result = NS_OK;
358 }
359 return result;
360}
361
362
363/*
364 *----------------------------------------------------------------------
365 *
366 * Ns_TclCreateInterp --
367 *
368 * Create a new interp with basic commands.
369 *
370 * Results:
371 * Pointer to new interp.
372 *
373 * Side effects:
374 * Depends on Tcl library init scripts.
375 *
376 *----------------------------------------------------------------------
377 */
378
379Tcl_Interp *
380Ns_TclCreateInterp(void)
381{
382 return NsTclAllocateInterp(NULL((void*)0));
383}
384
385
386/*
387 *----------------------------------------------------------------------
388 *
389 * Ns_TclInit --
390 *
391 * Initialize the given interp with basic commands.
392 *
393 * Results:
394 * Always TCL_OK.
395 *
396 * Side effects:
397 * Depends on Tcl library init scripts.
398 *
399 *----------------------------------------------------------------------
400 */
401
402int
403Ns_TclInit(Tcl_Interp *interp)
404{
405 NsServer *servPtr = NsGetServer(NULL((void*)0));
406
407 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
408
409 /*
410 * Associate the interp data with the current interpreter.
411 */
412 (void)NewInterpData(interp, servPtr);
413
414 return TCL_OK0;
415}
416
417
418/*
419 *----------------------------------------------------------------------
420 *
421 * Ns_TclEval --
422 *
423 * Execute a Tcl script in the context of the given server.
424 *
425 * Results:
426 * NaviServer result code. String result or error placed in dsPtr if
427 * dsPtr is not NULL.
428 *
429 * Side effects:
430 * Tcl interp may be allocated, initialized and cached if none
431 * available.
432 *
433 *----------------------------------------------------------------------
434 */
435
436Ns_ReturnCode
437Ns_TclEval(Ns_DStringTcl_DString *dsPtr, const char *server, const char *script)
438{
439 Tcl_Interp *interp;
440 Ns_ReturnCode status = NS_ERROR;
441
442 NS_NONNULL_ASSERT(script != NULL)((void) (0));
443
444 interp = Ns_TclAllocateInterp(server);
445 if (interp != NULL((void*)0)) {
446 const char *result;
447
448 if (Tcl_EvalEx(interp, script, -1, 0) != TCL_OK0) {
449 result = Ns_TclLogErrorInfo(interp, NULL((void*)0));
450 } else {
451 result = Tcl_GetStringResult(interp);
452 status = NS_OK;
453 }
454 if (dsPtr != NULL((void*)0)) {
455 Ns_DStringAppend(dsPtr, result)Tcl_DStringAppend((dsPtr), (result), -1);
456 }
457 Ns_TclDeAllocateInterp(interp);
458 }
459 return status;
460}
461
462
463/*
464 *----------------------------------------------------------------------
465 *
466 * Ns_TclAllocateInterp, NsTclAllocateInterp --
467 *
468 * Return a pre-initialized interp for the given server or create
469 * a new one and cache it for the current thread.
470 *
471 * Results:
472 * Pointer to Tcl_Interp or NULL if invalid server.
473 *
474 * Side effects:
475 * May invoke alloc and create traces.
476 *
477 *----------------------------------------------------------------------
478 */
479
480Tcl_Interp *
481Ns_TclAllocateInterp(const char *server)
482{
483 Tcl_Interp *result = NULL((void*)0);
484
485 /*
486 * Verify the server. NULL (i.e., no server) is valid but
487 * a non-null, unknown server is an error.
488 */
489 if (server == NULL((void*)0)) {
490 result = PopInterp(NULL((void*)0), NULL((void*)0))->interp;
491
492 } else {
493 NsServer *servPtr = NsGetServer(server);
494 if (likely( servPtr != NULL)(__builtin_expect((servPtr != ((void*)0)), 1)) ) {
495 result = PopInterp(servPtr, NULL((void*)0))->interp;
496 }
497 }
498
499 return result;
500}
501
502Tcl_Interp *
503NsTclAllocateInterp(NsServer *servPtr)
504{
505 const NsInterp *itPtr = PopInterp(servPtr, NULL((void*)0));
506
507 return itPtr->interp;
508}
509
510
511/*
512 *----------------------------------------------------------------------
513 *
514 * Ns_TclDeAllocateInterp --
515 *
516 * Return an interp to the per-thread cache. If the interp is
517 * associated with a connection, simply adjust the refcnt as
518 * cleanup will occur later when the connection closes.
519 *
520 * Results:
521 * None.
522 *
523 * Side effects:
524 * See notes on garbage collection in PushInterp.
525 *
526 *----------------------------------------------------------------------
527 */
528
529void
530Ns_TclDeAllocateInterp(Tcl_Interp *interp)
531{
532 NsInterp *itPtr;
533
534 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
535
536 itPtr = NsGetInterpData(interp);
537 if (itPtr == NULL((void*)0)) {
538 Ns_Log(Bug, "Ns_TclDeAllocateInterp: no interp data");
539 Tcl_DeleteInterp(interp);
540 } else if (itPtr->conn == NULL((void*)0)) {
541 PushInterp(itPtr);
542 } else {
543 itPtr->refcnt--;
544 }
545}
546
547
548/*
549 *----------------------------------------------------------------------
550 *
551 * Ns_GetConnInterp --
552 *
553 * Get an interp for the given connection. The interp will be
554 * automatically cleaned up at the end of the connection via a
555 * call to NsFreeConnInterp().
556 *
557 * Results:
558 * Pointer to Tcl_interp.
559 *
560 * Side effects:
561 * Interp may be allocated, initialized and cached. Interp traces
562 * may run.
563 *
564 *----------------------------------------------------------------------
565 */
566
567Tcl_Interp *
568Ns_GetConnInterp(Ns_Conn *conn)
569{
570 Conn *connPtr;
571 NsInterp *itPtr;
572
573 NS_NONNULL_ASSERT(conn != NULL)((void) (0));
574 connPtr = (Conn *) conn;
575
576 if (connPtr->itPtr == NULL((void*)0)) {
577 itPtr = PopInterp(connPtr->poolPtr->servPtr, NULL((void*)0));
578 itPtr->conn = conn;
579 itPtr->nsconn.flags = 0u;
580 connPtr->itPtr = itPtr;
581 RunTraces(itPtr, NS_TCL_TRACE_GETCONN);
582 }
583 return connPtr->itPtr->interp;
584}
585
586void
587NsIdleCallback(NsServer *servPtr)
588{
589 NsInterp *itPtr;
590
591 NS_NONNULL_ASSERT(servPtr != NULL)((void) (0));
592
593 itPtr = PopInterp(servPtr, NULL((void*)0));
594 itPtr->nsconn.flags = 0u;
595 RunTraces(itPtr, NS_TCL_TRACE_IDLE);
596 PushInterp(itPtr);
597}
598
599
600/*
601 *----------------------------------------------------------------------
602 *
603 * Ns_FreeConnInterp --
604 *
605 * Deprecated. See: NsFreeConnInterp.
606 *
607 * Results:
608 * None.
609 *
610 * Side effects:
611 * None.
612 *
613 *----------------------------------------------------------------------
614 */
615
616void
617Ns_FreeConnInterp(Ns_Conn *UNUSED(conn)UNUSED_conn __attribute__((__unused__)))
618{
619 return;
620}
621
622
623/*
624 *----------------------------------------------------------------------
625 *
626 * Ns_TclGetConn --
627 *
628 * Get the Ns_Conn structure associated with an interp.
629 *
630 * Results:
631 * Pointer to Ns_Conn or NULL.
632 *
633 * Side effects:
634 * None.
635 *
636 *----------------------------------------------------------------------
637 */
638
639Ns_Conn *
640Ns_TclGetConn(Tcl_Interp *interp)
641{
642 const NsInterp *itPtr;
643
644 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
645
646 itPtr = NsGetInterpData(interp);
647 return ((itPtr != NULL((void*)0)) ? itPtr->conn : NULL((void*)0));
648}
649
650
651/*
652 *----------------------------------------------------------------------
653 *
654 * Ns_TclDestroyInterp --
655 *
656 * Delete an interp.
657 *
658 * Results:
659 * None.
660 *
661 * Side effects:
662 * Depends on delete traces, if any.
663 *
664 *----------------------------------------------------------------------
665 */
666
667void
668Ns_TclDestroyInterp(Tcl_Interp *interp)
669{
670 NsInterp *itPtr;
671
672 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
673
674 itPtr = NsGetInterpData(interp);
675 /*
676 * If this is a NaviServer interp, clean it up
677 */
678
679 if (itPtr != NULL((void*)0)) {
680 Tcl_HashTable *tablePtr = Ns_TlsGet(&tls);
681
682 /*
683 * Run traces (behaves gracefully, if there is no server
684 * associated).
685 */
686 RunTraces(itPtr, NS_TCL_TRACE_DELETE);
687
688 /*
689 * During shutdown, don't fetch entries via GetCacheEntry(),
690 * since this function might create new cache entries. Note,
691 * that the thread local cache table might contain as well
692 * entries with itPtr->servPtr == NULL.
693 */
694 if (tablePtr != NULL((void*)0)) {
695 int ignored;
696 Tcl_HashEntry *hPtr;
697
698 /*
699 * Make sure to delete the entry in the thread local cache to
700 * avoid double frees in DeleteInterps()
701 */
702
703 hPtr = Tcl_CreateHashEntry(tablePtr, (char *)itPtr->servPtr, &ignored)(*((tablePtr)->createProc))(tablePtr, (const char *)((char
*)itPtr->servPtr), &ignored)
;
704 Tcl_SetHashValue(hPtr, NULL)((hPtr)->clientData = (ClientData) (((void*)0)));
705 }
706 }
707
708 /*
709 * All other cleanup, including the NsInterp data, if any, will
710 * be handled by Tcl's normal delete mechanisms.
711 */
712
713 Tcl_DeleteInterp(interp);
714}
715
716
717/*
718 *----------------------------------------------------------------------
719 *
720 * Ns_TclMarkForDelete --
721 *
722 * Mark the interp to be deleted after next cleanup. This routine
723 * is useful for destroy interps after they've been modified in
724 * weird ways, e.g., by the TclPro debugger.
725 *
726 * Results:
727 * None.
728 *
729 * Side effects:
730 * Interp will be deleted on next de-allocate.
731 *
732 *----------------------------------------------------------------------
733 */
734
735void
736Ns_TclMarkForDelete(Tcl_Interp *interp)
737{
738 NsInterp *itPtr;
739
740 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
741
742 itPtr = NsGetInterpData(interp);
743 if (itPtr != NULL((void*)0)) {
744 itPtr->deleteInterp = NS_TRUE1;
745 }
746}
747
748
749/*
750 *----------------------------------------------------------------------
751 *
752 * Ns_TclRegisterTrace --
753 *
754 * Add an interp trace. Traces are called in FIFO order. Valid
755 * traces are: NS_TCL_TRACE... CREATE, DELETE, ALLOCATE,
756 * DEALLOCATE, GETCONN, and FREECONN.
757 *
758 * Results:
759 * NS_OK if called with a non-NULL server before startup has
760 * completed, NS_ERROR otherwise.
761 *
762 * Side effects:
763 * CREATE and ALLOCATE traces are run immediately in the current
764 * interp (the initial bootstrap interp).
765 *
766 *----------------------------------------------------------------------
767 */
768
769Ns_ReturnCode
770Ns_TclRegisterTrace(const char *server, Ns_TclTraceProc *proc,
771 const void *arg, Ns_TclTraceType when)
772{
773
774 NsServer *servPtr;
775 Ns_ReturnCode status = NS_OK;
776
777 NS_NONNULL_ASSERT(server != NULL)((void) (0));
778 NS_NONNULL_ASSERT(proc != NULL)((void) (0));
779
780 servPtr = NsGetServer(server);
781 if (servPtr == NULL((void*)0)) {
782 Ns_Log(Error, "Ns_TclRegisterTrace: Invalid server: %s", server);
783 status = NS_ERROR;
784
785 } else if (Ns_InfoStarted()) {
786 Ns_Log(Error, "Can not register Tcl trace, server already started.");
787 status = NS_ERROR;
788
789 } else {
790 TclTrace *tracePtr = ns_malloc(sizeof(TclTrace));
791
792 tracePtr->proc = proc;
793 tracePtr->arg = arg;
794 tracePtr->when = when;
795 tracePtr->nextPtr = NULL((void*)0);
796
797 tracePtr->prevPtr = servPtr->tcl.lastTracePtr;
798 servPtr->tcl.lastTracePtr = tracePtr;
799 if (tracePtr->prevPtr != NULL((void*)0)) {
800 tracePtr->prevPtr->nextPtr = tracePtr;
801 } else {
802 servPtr->tcl.firstTracePtr = tracePtr;
803 }
804
805 /*
806 * Run CREATE and ALLOCATE traces immediately so that commands registered
807 * by binary modules can be called by Tcl init scripts sourced by the
808 * already initialized interp which loads the modules.
809 */
810
811 if ((when == NS_TCL_TRACE_CREATE) || (when == NS_TCL_TRACE_ALLOCATE)) {
812 Tcl_Interp *interp = NsTclAllocateInterp(servPtr);
813
814 if ((*proc)(interp, arg) != NS_OK) {
815 (void) Ns_TclLogErrorInfo(interp, "\n(context: register trace)");
816 }
817 Ns_TclDeAllocateInterp(interp);
818 }
819 }
820 return status;
821}
822
823
824/*
825 *----------------------------------------------------------------------
826 *
827 * Ns_TclRegisterAtCreate, Ns_TclRegisterAtCleanup,
828 * Ns_TclRegisterAtDelete --
829 *
830 * Register callbacks for interp create, cleanup, and delete at
831 * startup. These routines are deprecated in favor of the more
832 * general Ns_TclRegisterTrace. In particular, they do not take a
833 * virtual server argument so must assume the currently
834 * initializing server is the intended server.
835 *
836 * Deprecated.
837 *
838 * Results:
839 * See Ns_TclRegisterTrace.
840 *
841 * Side effects:
842 * See Ns_TclRegisterTrace.
843 *
844 *----------------------------------------------------------------------
845 */
846
847Ns_ReturnCode
848Ns_TclRegisterAtCreate(Ns_TclTraceProc *proc, const void *arg)
849{
850 return RegisterAt(proc, arg, NS_TCL_TRACE_CREATE);
851}
852
853Ns_ReturnCode
854Ns_TclRegisterAtCleanup(Ns_TclTraceProc *proc, const void *arg)
855{
856 return RegisterAt(proc, arg, NS_TCL_TRACE_DEALLOCATE);
857}
858
859Ns_ReturnCode
860Ns_TclRegisterAtDelete(Ns_TclTraceProc *proc, const void *arg)
861{
862 return RegisterAt(proc, arg, NS_TCL_TRACE_DELETE);
863}
864
865static Ns_ReturnCode
866RegisterAt(Ns_TclTraceProc *proc, const void *arg, Ns_TclTraceType when)
867{
868 const NsServer *servPtr;
869 Ns_ReturnCode status;
870
871 NS_NONNULL_ASSERT(proc != NULL)((void) (0));
872
873 servPtr = NsGetInitServer();
874 if (servPtr == NULL((void*)0)) {
875 status = NS_ERROR;
876 } else {
877 status = Ns_TclRegisterTrace(servPtr->server, proc, arg, when);
878 }
879 return status;
880}
881
882
883/*
884 *----------------------------------------------------------------------
885 *
886 * Ns_TclInitInterps --
887 *
888 * Arrange for the given proc to be called on newly created
889 * interps. This routine now simply uses the more general Tcl
890 * interp tracing facility. Earlier versions would invoke the
891 * given proc immediately on each interp in a shared pool which
892 * explains this otherwise misnamed API.
893 *
894 * Deprecated.
895 *
896 * Results:
897 * See Ns_TclRegisterTrace.
898 *
899 * Side effects:
900 * See Ns_TclRegisterTrace.
901 *
902 *----------------------------------------------------------------------
903 */
904
905Ns_ReturnCode
906Ns_TclInitInterps(const char *server, Ns_TclInterpInitProc *proc, const void *arg)
907{
908 return Ns_TclRegisterTrace(server, proc, arg, NS_TCL_TRACE_CREATE);
909}
910
911
912/*
913 *----------------------------------------------------------------------
914 *
915 * Ns_TclRegisterDeferred --
916 *
917 * Register a procedure to be called when the interp is deallocated.
918 * This is a one-shot FIFO order callback mechanism which is seldom
919 * used.
920 *
921 * Deprecated.
922 *
923 * Results:
924 * None.
925 *
926 * Side effects:
927 * Procedure will be called later.
928 *
929 *----------------------------------------------------------------------
930 */
931
932void
933Ns_TclRegisterDeferred(Tcl_Interp *interp, Ns_TclDeferProc *proc, void *arg)
934{
935 NsInterp *itPtr = NsGetInterpData(interp);
936
937 if (itPtr != NULL((void*)0)) {
938 Defer *deferPtr, **nextPtrPtr;
939
940 deferPtr = ns_malloc(sizeof(Defer));
941 deferPtr->proc = proc;
942 deferPtr->arg = arg;
943 deferPtr->nextPtr = NULL((void*)0);
944 nextPtrPtr = &itPtr->firstDeferPtr;
945 while (*nextPtrPtr != NULL((void*)0)) {
946 nextPtrPtr = &((*nextPtrPtr)->nextPtr);
947 }
948 *nextPtrPtr = deferPtr;
949 }
950}
951
952
953/*
954 *----------------------------------------------------------------------
955 *
956 * Ns_TclLibrary --
957 *
958 * Return the name of the private Tcl lib if configured, or the
959 * global shared library otherwise.
960 *
961 * Results:
962 * Tcl lib name.
963 *
964 * Side effects:
965 * None.
966 *
967 *----------------------------------------------------------------------
968 */
969
970const char *
971Ns_TclLibrary(const char *server)
972{
973 const NsServer *servPtr = NsGetServer(server);
974
975 return ((servPtr != NULL((void*)0)) ? servPtr->tcl.library : nsconf.tcl.sharedlibrary);
976}
977
978
979/*
980 *----------------------------------------------------------------------
981 *
982 * Ns_TclInterpServer --
983 *
984 * Return the name of the server.
985 *
986 * Results:
987 * Server name, or NULL if not a server interp.
988 *
989 * Side effects:
990 * None.
991 *
992 *----------------------------------------------------------------------
993 */
994
995const char *
996Ns_TclInterpServer(Tcl_Interp *interp)
997{
998 const NsInterp *itPtr;
999 const char *result = NULL((void*)0);
1000
1001 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
1002
1003 itPtr = NsGetInterpData(interp);
1004 if (itPtr != NULL((void*)0) && itPtr->servPtr != NULL((void*)0)) {
1005 result = itPtr->servPtr->server;
1006 }
1007 return result;
1008}
1009
1010
1011/*
1012 *----------------------------------------------------------------------
1013 *
1014 * Ns_TclInitModule --
1015 *
1016 * Add a module name to the init list.
1017 *
1018 * Results:
1019 * NS_ERROR if no such server, NS_OK otherwise.
1020 *
1021 * Side effects:
1022 * Module will be initialized by the init script later.
1023 *
1024 *----------------------------------------------------------------------
1025 */
1026
1027Ns_ReturnCode
1028Ns_TclInitModule(const char *server, const char *module)
1029{
1030 const NsServer *servPtr;
1031 Ns_ReturnCode status;
1032
1033 NS_NONNULL_ASSERT(server != NULL)((void) (0));
1034 NS_NONNULL_ASSERT(module != NULL)((void) (0));
1035
1036 servPtr = NsGetServer(server);
1037 if (servPtr == NULL((void*)0)) {
1038 status = NS_ERROR;
1039 } else {
1040 (void) Tcl_ListObjAppendElement(NULL((void*)0), servPtr->tcl.modules,
1041 Tcl_NewStringObj(module, -1));
1042 status = NS_OK;
1043 }
1044 return status;
1045}
1046
1047
1048
1049/*
1050 *----------------------------------------------------------------------
1051 *
1052 * ICtlAddTrace
1053 *
1054 * Helper function for various trace commands
1055 *
1056 * Results:
1057 * Standard Tcl result.
1058 *
1059 * Side effects:
1060 * Adding a trace on success.
1061 *
1062 *----------------------------------------------------------------------
1063 */
1064
1065static int
1066ICtlAddTrace(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv, Ns_TclTraceType when)
1067{
1068 unsigned int flags = 0u;
1069 Tcl_Obj *scriptObj = NULL((void*)0);
1070 int remain = 0, result = TCL_OK0;
1071 Ns_ReturnCode status;
1072
1073 if (when == NS_TCL_TRACE_NONE) {
1074 Ns_ObjvSpec addTraceArgs[] = {
1075 {"when", Ns_ObjvFlags, &flags, traceWhen},
1076 {"script", Ns_ObjvObj, &scriptObj, NULL((void*)0)},
1077 {"?args", Ns_ObjvArgs, &remain, NULL((void*)0)},
1078 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
1079 };
1080 status = Ns_ParseObjv(NULL((void*)0), addTraceArgs, interp, 2, objc, objv);
1081 } else {
1082 Ns_ObjvSpec legacyAddTraceArgs[] = {
1083 {"script", Ns_ObjvObj, &scriptObj, NULL((void*)0)},
1084 {"?args", Ns_ObjvArgs, &remain, NULL((void*)0)},
1085 };
1086 status = Ns_ParseObjv(NULL((void*)0), legacyAddTraceArgs, interp, 2, objc, objv);
1087 }
1088 if (status != NS_OK) {
1089 result = TCL_ERROR1;
1090 } else {
1091 const NsInterp *itPtr = (const NsInterp *)clientData;
1092 const NsServer *servPtr = itPtr->servPtr;
1093
1094 if (servPtr != NsGetInitServer()) {
1095 Ns_TclPrintfResult(interp, "cannot add module after server startup");
1096 result = TCL_ERROR1;
1097
1098 } else {
1099 const Ns_TclCallback *cbPtr;
1100
1101 /*
1102 * When NS_TCL_TRACE_NONE was provide, get the value from the
1103 * parsed flags.
1104 */
1105 if (when == NS_TCL_TRACE_NONE) {
1106 when = (Ns_TclTraceType)flags;
1107 }
1108 cbPtr = Ns_TclNewCallback(interp, (ns_funcptr_t)NsTclTraceProc,
1109 scriptObj, remain, objv + (objc - remain));
1110 if (Ns_TclRegisterTrace(servPtr->server, NsTclTraceProc, cbPtr, when) != NS_OK) {
1111 result = TCL_ERROR1;
1112 }
1113 }
1114 }
1115 return result;
1116}
1117
1118
1119
1120/*
1121 *----------------------------------------------------------------------
1122 *
1123 * ICtlAddModuleObjCmd - subcommand of NsTclICtlObjCmd --
1124 *
1125 * Implements "ns_ictl addmodule" command.
1126 * Add a Tcl module to the list for later initialization.
1127 *
1128 * Results:
1129 * Standard Tcl result.
1130 *
1131 * Side effects:
1132 * Add module.
1133 *
1134 *----------------------------------------------------------------------
1135 */
1136static int
1137ICtlAddModuleObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1138{
1139 const NsInterp *itPtr = (const NsInterp *)clientData;
1140 const NsServer *servPtr = itPtr->servPtr;
1141 Tcl_Obj *moduleObj;
1142 int result;
1143 Ns_ObjvSpec args[] = {
1144 {"module", Ns_ObjvObj, &moduleObj, NULL((void*)0)},
1145 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
1146 };
1147
1148 if (Ns_ParseObjv(NULL((void*)0), args, interp, 2, objc, objv) != NS_OK) {
1149 result = TCL_ERROR1;
1150
1151 } else if (servPtr != NsGetInitServer()) {
1152 Ns_TclPrintfResult(interp, "cannot add module after server startup");
1153 result = TCL_ERROR1;
1154
1155 } else {
1156 result = Tcl_ListObjAppendElement(interp, servPtr->tcl.modules, moduleObj);
1157 if (result == TCL_OK0) {
1158 Tcl_SetObjResult(interp, servPtr->tcl.modules);
1159 }
1160 }
1161 return result;
1162}
1163
1164
1165/*
1166 *----------------------------------------------------------------------
1167 *
1168 * ICtlGetObjCmd - subcommand of NsTclICtlObjCmd --
1169 *
1170 * Implements "ns_ictl get" command.
1171 * Get the current init script to evaluate in new interps.
1172 *
1173 * Results:
1174 * Standard Tcl result.
1175 *
1176 * Side effects:
1177 * None.
1178 *
1179 *----------------------------------------------------------------------
1180 */
1181
1182static int
1183ICtlGetObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1184{
1185 const NsInterp *itPtr = (const NsInterp *)clientData;
1186 NsServer *servPtr = itPtr->servPtr;
1187 int result = TCL_OK0;
1188
1189 if (Ns_ParseObjv(NULL((void*)0), NULL((void*)0), interp, 2, objc, objv) != NS_OK) {
1190 result = TCL_ERROR1;
1191
1192 } else {
1193 Ns_RWLockRdLock(&servPtr->tcl.lock);
1194 Tcl_SetObjResult(interp, Tcl_NewStringObj(servPtr->tcl.script, -1));
1195 Ns_RWLockUnlock(&servPtr->tcl.lock);
1196 }
1197 return result;
1198}
1199
1200
1201/*
1202 *----------------------------------------------------------------------
1203 *
1204 * ICtlGetModulesObjCmd - subcommand of NsTclICtlObjCmd --
1205 *
1206 * Implements "ns_ictl getmodules" command.
1207 * Return the list of registered modules.
1208 *
1209 * Results:
1210 * Standard Tcl result.
1211 *
1212 * Side effects:
1213 * None.
1214 *
1215 *----------------------------------------------------------------------
1216 */
1217
1218static int
1219ICtlGetModulesObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1220{
1221 const NsInterp *itPtr = (const NsInterp *)clientData;
1222 const NsServer *servPtr = itPtr->servPtr;
1223 int result = TCL_OK0;
1224
1225 if (Ns_ParseObjv(NULL((void*)0), NULL((void*)0), interp, 2, objc, objv) != NS_OK) {
1226 result = TCL_ERROR1;
1227
1228 } else {
1229 Tcl_SetObjResult(interp, servPtr->tcl.modules);
1230 }
1231 return result;
1232}
1233
1234
1235
1236/*
1237 *----------------------------------------------------------------------
1238 *
1239 * ICtlEpochObjCmd - subcommand of NsTclICtlObjCmd --
1240 *
1241 * Implements "ns_ictl epoch" command.
1242 * Check the version of this interp against current init script.
1243 *
1244 * Results:
1245 * Standard Tcl result.
1246 *
1247 * Side effects:
1248 * None.
1249 *
1250 *----------------------------------------------------------------------
1251 */
1252static int
1253ICtlEpochObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1254{
1255 const NsInterp *itPtr = (const NsInterp *)clientData;
1256 NsServer *servPtr = itPtr->servPtr;
1257 int result = TCL_OK0;
1258
1259 if (Ns_ParseObjv(NULL((void*)0), NULL((void*)0), interp, 2, objc, objv) != NS_OK) {
1260 result = TCL_ERROR1;
1261
1262 } else {
1263 Ns_RWLockRdLock(&servPtr->tcl.lock);
1264 Tcl_SetObjResult(interp, Tcl_NewIntObj(servPtr->tcl.epoch));
1265 Ns_RWLockUnlock(&servPtr->tcl.lock);
1266 }
1267 return result;
1268}
1269
1270
1271/*
1272 *----------------------------------------------------------------------
1273 *
1274 * ICtlMaxconcurrentupdatesObjCmd - subcommand of NsTclICtlObjCmd --
1275 *
1276 * Implements "ns_ictl maxconcurrentupdates" command. Sets or queries the
1277 * number for allowed concurrent updates, when epoch counter is
1278 * incremented.
1279 *
1280 * Results:
1281 * Standard Tcl result.
1282 *
1283 * Side effects:
1284 * None.
1285 *
1286 *----------------------------------------------------------------------
1287 */
1288static int
1289ICtlMaxconcurrentupdatesObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1290{
1291 int result = TCL_OK0, maxValue = -1;
1292 Ns_ObjvValueRange posIntRange1 = {1, INT_MAX2147483647};
1293 Ns_ObjvSpec args[] = {
1294 {"?max", Ns_ObjvInt, &maxValue, &posIntRange1},
1295 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
1296 };
1297
1298 if (Ns_ParseObjv(NULL((void*)0), args, interp, 2, objc, objv) != NS_OK) {
1299 result = TCL_ERROR1;
1300
1301 } else {
1302 Ns_MutexLock(&updateLock);
1303 if (maxValue != -1) {
1304 maxConcurrentUpdates = maxValue;
1305 } else {
1306 maxValue = maxConcurrentUpdates;
1307 }
1308 Ns_MutexUnlock(&updateLock);
1309
1310 Tcl_SetObjResult(interp, Tcl_NewIntObj(maxValue));
1311 }
1312
1313 return result;
1314}
1315
1316
1317/*
1318 *----------------------------------------------------------------------
1319 *
1320 * ICtlMarkForDeleteObjCmd - subcommand of NsTclICtlObjCmd --
1321 *
1322 * Implements "ns_ictl markfordelete" command.
1323 * The interp will be deleted on next deallocation.
1324 *
1325 * Results:
1326 * Standard Tcl result.
1327 *
1328 * Side effects:
1329 * Adding flag to itPtr.
1330 *
1331 *----------------------------------------------------------------------
1332 */
1333
1334static int
1335ICtlMarkForDeleteObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1336{
1337 NsInterp *itPtr = (NsInterp *)clientData;
1338 int result = TCL_OK0;
1339
1340 if (Ns_ParseObjv(NULL((void*)0), NULL((void*)0), interp, 2, objc, objv) != NS_OK) {
1341 result = TCL_ERROR1;
1342 } else {
1343 itPtr->deleteInterp = NS_TRUE1;
1344 }
1345 return result;
1346}
1347
1348
1349/*
1350 *----------------------------------------------------------------------
1351 *
1352 * ICtlSaveObjCmd - subcommand of NsTclICtlObjCmd --
1353 *
1354 * Implements "ns_ictl save" command.
1355 * Save the init script.
1356 *
1357 * Results:
1358 * Standard Tcl result.
1359 *
1360 * Side effects:
1361 * Save bluprint.
1362 *
1363 *----------------------------------------------------------------------
1364 */
1365static int
1366ICtlSaveObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1367{
1368 int result = TCL_OK0;
1369 Tcl_Obj *scriptObj;
1370 Ns_ObjvSpec args[] = {
1371 {"script", Ns_ObjvObj, &scriptObj, NULL((void*)0)},
1372 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
1373 };
1374
1375 if (Ns_ParseObjv(NULL((void*)0), args, interp, 2, objc, objv) != NS_OK) {
1376 result = TCL_ERROR1;
1377
1378 } else {
1379 const NsInterp *itPtr = (const NsInterp *)clientData;
1380 NsServer *servPtr = itPtr->servPtr;
1381 int length;
1382 const char *script = ns_strdup(Tcl_GetStringFromObj(scriptObj, &length));
1383
1384 Ns_RWLockWrLock(&servPtr->tcl.lock);
1385 ns_free((char *)servPtr->tcl.script);
1386 servPtr->tcl.script = script;
1387 servPtr->tcl.length = length;
1388 if (++servPtr->tcl.epoch == 0) {
1389 /*
1390 * Epoch zero is reserved for new interps.
1391 */
1392 ++itPtr->servPtr->tcl.epoch;
1393 }
1394 Ns_RWLockUnlock(&servPtr->tcl.lock);
1395 }
1396 return result;
1397}
1398
1399/*
1400 *----------------------------------------------------------------------
1401 *
1402 * ICtlUpdateObjCmd - subcommand of NsTclICtlObjCmd --
1403 *
1404 * Implements "ns_ictl update" command.
1405 * Check for and process possible change in the init script.
1406 *
1407 * Results:
1408 * Standard Tcl result.
1409 *
1410 * Side effects:
1411 * Update blueprint.
1412 *
1413 *----------------------------------------------------------------------
1414 */
1415
1416static int
1417ICtlUpdateObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1418{
1419 NsInterp *itPtr = (NsInterp *)clientData;
1420 int result;
1421
1422 if (Ns_ParseObjv(NULL((void*)0), NULL((void*)0), interp, 2, objc, objv) != NS_OK) {
1423 result = TCL_ERROR1;
1424
1425 } else {
1426 result = UpdateInterp(itPtr);
1427 }
1428 return result;
1429}
1430
1431
1432/*
1433 *----------------------------------------------------------------------
1434 *
1435 * ICtlCleanupObjCmd - subcommand of NsTclICtlObjCmd --
1436 *
1437 * Implements "ns_ictl cleanup" command.
1438 * Invoke the legacy defer callbacks.
1439 *
1440 * Results:
1441 * Standard Tcl result.
1442 *
1443 * Side effects:
1444 * Free memory.
1445 *
1446 *----------------------------------------------------------------------
1447 */
1448
1449static int
1450ICtlCleanupObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1451{
1452 NsInterp *itPtr = (NsInterp *)clientData;
1453 int result = TCL_OK0;
1454
1455 if (Ns_ParseObjv(NULL((void*)0), NULL((void*)0), interp, 2, objc, objv) != NS_OK) {
1456 result = TCL_ERROR1;
1457
1458 } else if (itPtr->firstDeferPtr != NULL((void*)0)) {
1459 Defer *deferPtr;
1460
1461 for (deferPtr = itPtr->firstDeferPtr; deferPtr != NULL((void*)0); deferPtr = deferPtr->nextPtr) {
1462 (*deferPtr->proc)(interp, deferPtr->arg);
1463 ns_free(deferPtr);
1464 }
1465 itPtr->firstDeferPtr = NULL((void*)0);
1466 result = UpdateInterp(itPtr);
1467 }
1468 return result;
1469}
1470
1471
1472/*
1473 *----------------------------------------------------------------------
1474 *
1475 * ICtlOnInitObjCmd
1476 * ICtlOnCreateObjCmd
1477 * ICtlOnCleanupObjCmd
1478 * ICtlOnDeleteObjCmd
1479 * ICtlTraceObjCmd
1480 * - subcommands of NsTclICtlObjCmd --
1481 *
1482 * Implements various trace commands
1483 *
1484 * "ns_ictl oncleanup"
1485 * "ns_ictl oncreate
1486 * "ns_ictl ondelete"
1487 * "ns_ictl oninit"
1488 * "ns_ictl trace"
1489 *
1490 * Register script-level interp traces. "ns_ictl trace" is the new
1491 * version, the other ones are deprecated 3-argument variants.
1492 *
1493 * Results:
1494 * Standard Tcl result.
1495 *
1496 * Side effects:
1497 * Adding a trace on success.
1498 *
1499 *----------------------------------------------------------------------
1500 */
1501
1502static int
1503ICtlOnCreateObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1504{
1505 Ns_LogDeprecated(objv, 2, "ns_ictl trace create ...", NULL((void*)0));
1506 return ICtlAddTrace(clientData, interp, objc, objv, NS_TCL_TRACE_CREATE);
1507}
1508static int
1509ICtlOnCleanupObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1510{
1511 Ns_LogDeprecated(objv, 2, "ns_ictl trace deallocate ...", NULL((void*)0));
1512 return ICtlAddTrace(clientData, interp, objc, objv, NS_TCL_TRACE_DEALLOCATE);
1513}
1514static int
1515ICtlOnDeleteObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1516{
1517 Ns_LogDeprecated(objv, 2, "ns_ictl trace delete ...", NULL((void*)0));
1518 return ICtlAddTrace(clientData, interp, objc, objv, NS_TCL_TRACE_DELETE);
1519}
1520static int
1521ICtlTraceObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1522{
1523 /*
1524 * Passing NS_TCL_TRACE_NONE as last argument means to get the trace type
1525 * from the passed-in value
1526 */
1527 return ICtlAddTrace(clientData, interp, objc, objv, NS_TCL_TRACE_NONE);
1528}
1529
1530
1531/*
1532 *----------------------------------------------------------------------
1533 *
1534 * ICtlGetTracesObjCmd - subcommand of NsTclICtlObjCmd --
1535 *
1536 * Implements "ns_ictl gettraces" command.
1537 * Return the script of the specified trace.
1538 *
1539 * Results:
1540 * Standard Tcl result.
1541 *
1542 * Side effects:
1543 * None.
1544 *
1545 *----------------------------------------------------------------------
1546 */
1547static int
1548ICtlGetTracesObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1549{
1550 int result = TCL_OK0;
1551 unsigned int flags = 0u;
1552 Ns_ObjvSpec args[] = {
1553 {"when", Ns_ObjvFlags, &flags, traceWhen},
1554 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
1555 };
1556
1557 if (Ns_ParseObjv(NULL((void*)0), args, interp, 2, objc, objv) != NS_OK) {
1558 result = TCL_ERROR1;
1559
1560 } else {
1561 const NsInterp *itPtr = (const NsInterp *)clientData;
1562 const NsServer *servPtr = itPtr->servPtr;
1563 Ns_DStringTcl_DString ds;
1564 const TclTrace *tracePtr;
1565 Ns_TclTraceType when = (Ns_TclTraceType)flags;
1566
1567 Ns_DStringInitTcl_DStringInit(&ds);
1568 for (tracePtr = servPtr->tcl.firstTracePtr;
1569 (tracePtr != NULL((void*)0));
1570 tracePtr = tracePtr->nextPtr) {
1571 if (tracePtr->when == when) {
1572 Ns_GetProcInfo(&ds, (ns_funcptr_t)tracePtr->proc, tracePtr->arg);
1573 }
1574 }
1575 Tcl_DStringResult(interp, &ds);
1576 }
1577 return result;
1578}
1579
1580/*
1581 *----------------------------------------------------------------------
1582 *
1583 * ICtlRunTracesObjCmd - subcommand of NsTclICtlObjCmd --
1584 *
1585 * Implements "ns_ictl runtraces" command.
1586 * Run the specified trace.
1587 *
1588 * Results:
1589 * Standard Tcl result.
1590 *
1591 * Side effects:
1592 * None.
1593 *
1594 *----------------------------------------------------------------------
1595 */
1596static int
1597ICtlRunTracesObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1598{
1599 int result = TCL_OK0;
1600 unsigned int flags = 0u;
1601 Ns_ObjvSpec args[] = {
1602 {"when", Ns_ObjvFlags, &flags, traceWhen},
1603 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
1604 };
1605
1606 if (Ns_ParseObjv(NULL((void*)0), args, interp, 2, objc, objv) != NS_OK) {
1607 result = TCL_ERROR1;
1608
1609 } else {
1610 NsInterp *itPtr = (NsInterp *)clientData;
1611
1612 RunTraces(itPtr, (Ns_TclTraceType)flags);
1613 }
1614 return result;
1615}
1616
1617/*
1618 *----------------------------------------------------------------------
1619 *
1620 * NsTclICtlObjCmd --
1621 *
1622 * Implements "ns_ictl". This command is used to control interp state for
1623 * virtual server interps. This command provide internal control
1624 * functions required by the init.tcl script and is not intended to be
1625 * called by a user directly. It supports four activities:
1626 *
1627 * 1. Managing the list of "modules" to initialize.
1628 * 2. Saving the init script for evaluation with new interps.
1629 * 3. Checking for change of the init script.
1630 * 4. Register script-level traces.
1631 *
1632 * See init.tcl for details.
1633 *
1634 * Results:
1635 * Standard Tcl result.
1636 *
1637 * Side effects:
1638 * Depends on the subcommand.
1639 *
1640 *----------------------------------------------------------------------
1641 */
1642
1643int
1644NsTclICtlObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1645{
1646 const Ns_SubCmdSpec subcmds[] = {
1647 {"addmodule", ICtlAddModuleObjCmd},
1648 {"cleanup", ICtlCleanupObjCmd},
1649 {"epoch", ICtlEpochObjCmd},
1650 {"get", ICtlGetObjCmd},
1651 {"getmodules", ICtlGetModulesObjCmd},
1652 {"gettraces", ICtlGetTracesObjCmd},
1653 {"markfordelete", ICtlMarkForDeleteObjCmd},
1654 {"maxconcurrentupdates", ICtlMaxconcurrentupdatesObjCmd},
1655 {"oncleanup", ICtlOnCleanupObjCmd},
1656 {"oncreate", ICtlOnCreateObjCmd},
1657 {"ondelete", ICtlOnDeleteObjCmd},
1658 {"oninit", ICtlOnCreateObjCmd},
1659 {"runtraces", ICtlRunTracesObjCmd},
1660 {"save", ICtlSaveObjCmd},
1661 {"trace", ICtlTraceObjCmd},
1662 {"update", ICtlUpdateObjCmd},
1663 {NULL((void*)0), NULL((void*)0)}
1664 };
1665
1666 return Ns_SubcmdObjv(subcmds, clientData, interp, objc, objv);
1667}
1668
1669
1670/*
1671 *----------------------------------------------------------------------
1672 *
1673 * NsTclAtCloseObjCmd --
1674 *
1675 * Implements "ns_atclose".
1676 *
1677 * Results:
1678 * Tcl result.
1679 *
1680 * Side effects:
1681 * Script will be invoked when the connection is closed. Note
1682 * the connection may continue execution, e.g., with continued
1683 * ADP code, traces, etc.
1684 *
1685 *----------------------------------------------------------------------
1686 */
1687
1688int
1689NsTclAtCloseObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1690{
1691 NsInterp *itPtr = (NsInterp *)clientData;
1692 AtClose *atPtr;
1693 int result = TCL_OK0;
1694
1695 if (objc < 2) {
1696 Tcl_WrongNumArgs(interp, 1, objv, "script ?args?");
1697 result = TCL_ERROR1;
1698
1699 } else if (NsConnRequire(interp, NS_CONN_REQUIRE_ALL0x0007u, NULL((void*)0)) != NS_OK) {
1700 result = TCL_ERROR1;
1701
1702 } else {
1703 atPtr = ns_malloc(sizeof(AtClose));
1704 atPtr->nextPtr = itPtr->firstAtClosePtr;
1705 itPtr->firstAtClosePtr = atPtr;
1706 atPtr->objPtr = Tcl_ConcatObj(objc-1, objv+1);
1707 Tcl_IncrRefCount(atPtr->objPtr)++(atPtr->objPtr)->refCount;
1708 }
1709
1710 return result;
1711}
1712
1713
1714/*
1715 *----------------------------------------------------------------------
1716 *
1717 * NsTclRunAtClose --
1718 *
1719 * Run and then free any registered connection at-close scripts.
1720 *
1721 * Results:
1722 * None.
1723 *
1724 * Side effects:
1725 * None.
1726 *
1727 *----------------------------------------------------------------------
1728 */
1729
1730void
1731NsTclRunAtClose(NsInterp *itPtr)
1732{
1733 Tcl_Interp *interp;
1734 AtClose *atPtr, *nextPtr;
1735
1736 NS_NONNULL_ASSERT(itPtr != NULL)((void) (0));
1737
1738 interp = itPtr->interp;
1739
1740 for (atPtr = itPtr->firstAtClosePtr; atPtr != NULL((void*)0); atPtr = nextPtr) {
1741 assert(atPtr->objPtr != NULL)((void) (0));
1742 if (Tcl_EvalObjEx(interp, atPtr->objPtr, TCL_EVAL_DIRECT0x040000) != TCL_OK0) {
1743 (void) Ns_TclLogErrorInfo(interp, "\n(context: at close)");
1744 }
1745 Tcl_DecrRefCount(atPtr->objPtr)do { Tcl_Obj *_objPtr = (atPtr->objPtr); if (_objPtr->refCount
-- <= 1) { TclFreeObj(_objPtr); } } while(0)
;
1746 nextPtr = atPtr->nextPtr;
1747 ns_free(atPtr);
1748 }
1749 itPtr->firstAtClosePtr = NULL((void*)0);
1750}
1751
1752
1753/*
1754 *----------------------------------------------------------------------
1755 *
1756 * NsTclInitServer --
1757 *
1758 * Evaluate server initialization script at startup.
1759 *
1760 * Results:
1761 * None.
1762 *
1763 * Side effects:
1764 * Depends on init script (normally init.tcl).
1765 *
1766 *----------------------------------------------------------------------
1767 */
1768
1769void
1770NsTclInitServer(const char *server)
1771{
1772 NsServer *servPtr;
1773
1774 NS_NONNULL_ASSERT(server != NULL)((void) (0));
1775
1776 servPtr = NsGetServer(server);
1777 if (servPtr != NULL((void*)0)) {
1778 Tcl_Interp *interp = NsTclAllocateInterp(servPtr);
1779
1780 if ( Tcl_FSEvalFile(interp, servPtr->tcl.initfile) != TCL_OK0) {
1781 (void) Ns_TclLogErrorInfo(interp, "\n(context: init server)");
1782 }
1783 Ns_TclDeAllocateInterp(interp);
1784 }
1785 Ns_ThreadSetName("-main:%s-", server);
1786}
1787
1788/*
1789 *----------------------------------------------------------------------
1790 *
1791 * NsTclAppInit --
1792 *
1793 * Initialize an interactive command interp with basic and
1794 * server commands using the default virtual server.
1795 *
1796 * Results:
1797 * Tcl result.
1798 *
1799 * Side effects:
1800 * Override Tcl exit command so that proper server shutdown
1801 * takes place.
1802 *
1803 *----------------------------------------------------------------------
1804 */
1805
1806int
1807NsTclAppInit(Tcl_Interp *interp)
1808{
1809 NsServer *servPtr;
1810 int result = TCL_OK0;
1811
1812 servPtr = NsGetServer(nsconf.defaultServer);
1813 if (servPtr == NULL((void*)0)) {
1814 Ns_Log(Bug, "NsTclAppInit: invalid default server: %s",
1815 nsconf.defaultServer);
1816 result = TCL_ERROR1;
1817
1818 } else if (Tcl_Init(interp) != TCL_OK0) {
1819 result = TCL_ERROR1;
1820
1821 } else {
1822 (void) Tcl_SetVar(interp, "tcl_rcFileName", "~/.nsdrc", TCL_GLOBAL_ONLY)Tcl_SetVar2(interp, "tcl_rcFileName", ((void*)0), "~/.nsdrc",
1)
;
1823 (void) Tcl_Eval(interp, "proc exit {} ns_shutdown");
1824 (void) PopInterp(servPtr, interp);
1825 }
1826
1827 return result;
1828}
1829
1830
1831/*
1832 *----------------------------------------------------------------------
1833 *
1834 * NsGetInterpData --
1835 *
1836 * Return the interp's NsInterp structure from assoc data.
1837 * This routine is used when the NsInterp is needed and
1838 * not available as command ClientData.
1839 *
1840 * Results:
1841 * Pointer to NsInterp or NULL if none.
1842 *
1843 * Side effects:
1844 * None.
1845 *
1846 *----------------------------------------------------------------------
1847 */
1848
1849NsInterp *
1850NsGetInterpData(Tcl_Interp *interp)
1851{
1852 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
1853 return Tcl_GetAssocData(interp, "ns:data", NULL((void*)0));
1854}
1855
1856
1857/*
1858 *----------------------------------------------------------------------
1859 *
1860 * NsFreeConnInterp --
1861 *
1862 * Free the interp data, if any, for given connection. This
1863 * routine is called at the end of connection processing.
1864 *
1865 * Results:
1866 * None.
1867 *
1868 * Side effects:
1869 * See PushInterp.
1870 *
1871 *----------------------------------------------------------------------
1872 */
1873
1874void
1875NsFreeConnInterp(Conn *connPtr)
1876{
1877 NsInterp *itPtr = connPtr->itPtr;
1878
1879 if (itPtr != NULL((void*)0)) {
1880 RunTraces(itPtr, NS_TCL_TRACE_FREECONN);
1881 itPtr->conn = NULL((void*)0);
1882 itPtr->nsconn.flags = 0u;
1883 PushInterp(itPtr);
1884 connPtr->itPtr = NULL((void*)0);
1885 }
1886}
1887
1888
1889/*
1890 *----------------------------------------------------------------------
1891 *
1892 * NsTclTraceProc --
1893 *
1894 * Eval a registered Tcl interp trace callback.
1895 *
1896 * Results:
1897 * Tcl result code from script eval.
1898 *
1899 * Side effects:
1900 * Depends on script.
1901 *
1902 *----------------------------------------------------------------------
1903 */
1904
1905int
1906NsTclTraceProc(Tcl_Interp *interp, const void *arg)
1907{
1908 const Ns_TclCallback *cbPtr = arg;
1909 int result;
1910
1911 result = Ns_TclEvalCallback(interp, cbPtr, NULL((void*)0), (char *)0L);
1912 if (unlikely(result != TCL_OK)(__builtin_expect((result != 0), 0))) {
1913 (void) Ns_TclLogErrorInfo(interp, "\n(context: trace proc)");
1914 }
1915
1916 return result;
1917}
1918
1919
1920/*
1921 *----------------------------------------------------------------------
1922 *
1923 * PopInterp --
1924 *
1925 * Get virtual-server interp from the per-thread cache and
1926 * increment the reference count. Allocate a new interp if
1927 * necessary.
1928 *
1929 * Results:
1930 * NsInterp.
1931 *
1932 * Side effects:
1933 * Will invoke alloc traces if not recursively allocated and, if
1934 * the interp is new, create traces.
1935 *
1936 *----------------------------------------------------------------------
1937 */
1938
1939static NsInterp *
1940PopInterp(NsServer *servPtr, Tcl_Interp *interp)
1941{
1942 NsInterp *itPtr;
1943 Tcl_HashEntry *hPtr;
1944 static Ns_Cs lock;
1945
1946 /*
1947 * Get an already initialized interp for the given virtual server
1948 * on this thread. If it doesn't yet exist, create and
1949 * initialize one.
1950 */
1951 hPtr = GetCacheEntry(servPtr);
1952 itPtr = Tcl_GetHashValue(hPtr)((hPtr)->clientData);
1953 if (itPtr == NULL((void*)0)) {
1954 if (nsconf.tcl.lockoninit) {
1955 Ns_CsEnter(&lock);
1956 }
1957 if (interp != NULL((void*)0)) {
1958 itPtr = NewInterpData(interp, servPtr);
1959 } else {
1960 interp = CreateInterp(&itPtr, servPtr);
1961 }
1962 if (servPtr != NULL((void*)0)) {
1963 itPtr->servPtr = servPtr;
1964 NsTclAddServerCmds(itPtr);
1965 RunTraces(itPtr, NS_TCL_TRACE_CREATE);
1966 if (UpdateInterp(itPtr) != TCL_OK0) {
1967 (void) Ns_TclLogErrorInfo(interp, "\n(context: update interpreter)");
1968 }
1969 } else {
1970 RunTraces(itPtr, NS_TCL_TRACE_CREATE);
1971 }
1972 if (nsconf.tcl.lockoninit) {
1973 Ns_CsLeave(&lock);
1974 }
1975 Tcl_SetHashValue(hPtr, itPtr)((hPtr)->clientData = (ClientData) (itPtr));
1976 }
1977
1978
1979 /*
1980 * Run allocation traces once.
1981 */
1982
1983 if (++itPtr->refcnt == 1) {
1984 RunTraces(itPtr, NS_TCL_TRACE_ALLOCATE);
1985 }
1986
1987 return itPtr;
1988}
1989
1990
1991/*
1992 *----------------------------------------------------------------------
1993 *
1994 * PushInterp --
1995 *
1996 * Return a virtual-server interp to the thread cache.
1997 *
1998 * Results:
1999 * None.
2000 *
2001 * Side effects:
2002 * May invoke de-alloc traces, destroy interp if no longer
2003 * being used.
2004 *
2005 *----------------------------------------------------------------------
2006 */
2007
2008static void
2009PushInterp(NsInterp *itPtr)
2010{
2011 Tcl_Interp *interp;
2012 bool_Bool ok = NS_TRUE1;
2013
2014 NS_NONNULL_ASSERT(itPtr != NULL)((void) (0));
2015
2016 interp = itPtr->interp;
2017
2018 /*
2019 * Evaluate the deallocation traces once to perform various garbage
2020 * collection and then either delete the interp or push it back on the
2021 * per-thread list.
2022 */
2023 if (itPtr->refcnt == 1) {
2024 RunTraces(itPtr, NS_TCL_TRACE_DEALLOCATE);
2025 if (itPtr->deleteInterp) {
2026 Ns_Log(Debug, "ns_markfordelete: true");
2027 Ns_TclDestroyInterp(interp);
2028 ok = NS_FALSE0;
2029 }
2030 }
2031 if (ok) {
2032 Tcl_ResetResult(interp);
2033 itPtr->refcnt--;
2034
2035 assert(itPtr->refcnt >= 0)((void) (0));
2036 }
2037}
2038
2039
2040/*
2041 *----------------------------------------------------------------------
2042 *
2043 * GetCacheEntry --
2044 *
2045 * Get hash entry in per-thread interp cache for given virtual
2046 * server.
2047 *
2048 * Results:
2049 * Pointer to hash entry.
2050 *
2051 * Side effects:
2052 * None.
2053 *
2054 *----------------------------------------------------------------------
2055 */
2056
2057static Tcl_HashEntry *
2058GetCacheEntry(const NsServer *servPtr)
2059{
2060 Tcl_HashTable *tablePtr;
2061 int ignored;
2062
2063 tablePtr = Ns_TlsGet(&tls);
2064 if (tablePtr == NULL((void*)0)) {
2065 tablePtr = ns_malloc(sizeof(Tcl_HashTable));
2066 Tcl_InitHashTable(tablePtr, TCL_ONE_WORD_KEYS(1));
2067 Ns_TlsSet(&tls, tablePtr);
2068 }
2069 return Tcl_CreateHashEntry(tablePtr, (char *) servPtr, &ignored)(*((tablePtr)->createProc))(tablePtr, (const char *)((char
*) servPtr), &ignored)
;
2070}
2071
2072
2073/*
2074 *----------------------------------------------------------------------
2075 *
2076 * NsTclCreateInterp --
2077 *
2078 * Create a fresh new Tcl interp. The creation is serialized to
2079 * prevent concurrent interp creations.
2080 *
2081 * Results:
2082 * Tcl_Interp pointer.
2083 *
2084 * Side effects:
2085 * Depends on Tcl library init scripts, errors will be logged.
2086 *
2087 *----------------------------------------------------------------------
2088 */
2089
2090Tcl_Interp *
2091NsTclCreateInterp(void) {
2092 Tcl_Interp *interp;
2093
2094 if (concurrent_interp_create) {
2095 interp = Tcl_CreateInterp();
2096 } else {
2097 Ns_MutexLock(&interpLock);
2098 interp = Tcl_CreateInterp();
2099 Ns_MutexUnlock(&interpLock);
2100 }
2101 return interp;
2102}
2103
2104
2105/*
2106 *----------------------------------------------------------------------
2107 *
2108 * CreateInterp --
2109 *
2110 * Create a fresh new Tcl interp configured for NaviServer
2111 *
2112 * Results:
2113 * Tcl_Interp pointer.
2114 *
2115 * Side effects:
2116 * Depends on Tcl library init scripts, errors will be logged.
2117 *
2118 *----------------------------------------------------------------------
2119 */
2120static Tcl_Interp *
2121CreateInterp(NsInterp **itPtrPtr, NsServer *servPtr)
2122{
2123 NsInterp *itPtr;
2124 Tcl_Interp *interp;
2125
2126 NS_NONNULL_ASSERT(itPtrPtr != NULL)((void) (0));
2127
2128 /*
2129 * Create and initialize a basic Tcl interp.
2130 */
2131
2132 interp = NsTclCreateInterp();
2133
2134 Tcl_InitMemory(interp);
2135 if (Tcl_Init(interp) != TCL_OK0) {
2136 (void) Ns_TclLogErrorInfo(interp, "\n(context: create interpreter)");
2137 }
2138
2139 /*
2140 * Make sure, the system encoding is UTF-8. Changing the system
2141 * encoding at run time is a potentially dangerous operation, since
2142 * Tcl might be loading already files based on a previous
2143 * encoding in another thread. So, we want to perform this
2144 * operation only once for all threads.
2145 */
2146 if (strcmp("utf-8", Tcl_GetEncodingName(Tcl_GetEncoding(interp, NULL((void*)0)))) != 0) {
2147 int result = Tcl_SetSystemEncoding(interp, "utf-8");
2148
2149 if (result != TCL_OK0) {
2150 (void) Ns_TclLogErrorInfo(interp, "\n(context: set system encoding to utf-8)");
2151 }
2152 }
2153
2154 /*
2155 * Allocate and associate a new NsInterp struct for the interp.
2156 */
2157
2158 itPtr = NewInterpData(interp, servPtr);
2159 itPtr->currentTrace = 0u;
2160 *itPtrPtr = itPtr;
2161
2162 return interp;
2163}
2164
2165
2166/*
2167 *----------------------------------------------------------------------
2168 *
2169 * NewInterpData --
2170 *
2171 * Create a new NsInterp struct for the given interp, adding
2172 * basic commands and associating it with the interp.
2173 *
2174 * Results:
2175 * Pointer to new NsInterp struct.
2176 *
2177 * Side effects:
2178 * Depends on Tcl init script sourced by Tcl_Init. Some Tcl
2179 * object types will be initialized on first call.
2180 *
2181 *----------------------------------------------------------------------
2182 */
2183
2184static NsInterp *
2185NewInterpData(Tcl_Interp *interp, NsServer *servPtr)
2186{
2187 static volatile bool_Bool initialized = NS_FALSE0;
2188 NsInterp *itPtr;
2189
2190 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
2191
2192 /*
2193 * Core one-time server initialization to add a few Tcl_Obj
2194 * types. These calls cannot be in NsTclInit above because
2195 * Tcl is not fully initialized at libnsd load time.
2196 */
2197
2198 if (!initialized) {
2199 Ns_MasterLock();
2200 if (!initialized) {
2201 NsTclInitQueueType();
2202 NsTclInitAddrType();
2203 NsTclInitTimeType();
2204 NsTclInitMemUnitType();
2205 NsTclInitKeylistType();
2206 initialized = NS_TRUE1;
2207 }
2208 Ns_MasterUnlock();
2209 }
2210
2211 /*
2212 * Allocate and initialize a new NsInterp struct.
2213 */
2214
2215 itPtr = NsGetInterpData(interp);
2216 if (itPtr == NULL((void*)0)) {
2217 itPtr = ns_calloc(1u, sizeof(NsInterp));
2218 itPtr->interp = interp;
2219 itPtr->servPtr = servPtr;
2220 Tcl_InitHashTable(&itPtr->sets, TCL_STRING_KEYS(0));
2221 Tcl_InitHashTable(&itPtr->chans, TCL_STRING_KEYS(0));
2222 Tcl_InitHashTable(&itPtr->httpRequests, TCL_STRING_KEYS(0));
2223 NsAdpInit(itPtr);
2224
2225 /*
2226 * Associate the new NsInterp with this interp. At interp delete
2227 * time, Tcl will call FreeInterpData to cleanup the struct.
2228 */
2229
2230 Tcl_SetAssocData(interp, "ns:data", FreeInterpData, itPtr);
2231
2232 /*
2233 * Add basic commands which function without a virtual server.
2234 */
2235
2236 NsTclAddBasicCmds(itPtr);
2237 }
2238
2239 return itPtr;
2240}
2241
2242
2243/*
2244 *----------------------------------------------------------------------
2245 *
2246 * UpdateInterp --
2247 *
2248 * Update the state of an interp by evaluating the saved script
2249 * whenever the epoch changes.
2250 *
2251 * Results:
2252 * Tcl result.
2253 *
2254 * Side effects:
2255 * None.
2256 *
2257 *----------------------------------------------------------------------
2258 */
2259
2260static int
2261UpdateInterp(NsInterp *itPtr)
2262{
2263 NsServer *servPtr;
2264 int result = TCL_OK0, epoch, scriptLength = 0;
2265 const char *script = NULL((void*)0);
2266 bool_Bool doUpdateNow = NS_FALSE0;
2267
2268 NS_NONNULL_ASSERT(itPtr != NULL)((void) (0));
2269 servPtr = itPtr->servPtr;
2270
2271 /*
2272 * A reader-writer lock is used on the assumption updates are rare and
2273 * likley expensive to evaluate if the virtual server contains significant
2274 * state. The Rd lock is here, since we are just reading the protected
2275 * variables.
2276 *
2277 * In the code block below, we want to avoid running the blueprint update
2278 * under the lock. Therefore, we copy the blueprint script with ns_strdup.
2279 */
2280 Ns_RWLockRdLock(&servPtr->tcl.lock);
2281 if (itPtr->epoch != servPtr->tcl.epoch) {
2282 epoch = servPtr->tcl.epoch;
2283 /*
2284 * The epoch has changed. Perform the interpreter update now, when
2285 * either (a) the interpreter is fresh, or (b) when the concurrently
2286 * running updates are below "maxConcurrentUpdates".
2287 */
2288 doUpdateNow = (itPtr->epoch < 1) || (concurrentUpdates < maxConcurrentUpdates);
2289 if (doUpdateNow) {
2290 concurrentUpdates++;
2291 script = ns_strdup(servPtr->tcl.script);
2292 scriptLength = servPtr->tcl.length;
2293 }
2294 } else {
2295 epoch = itPtr->epoch;
2296 }
2297 Ns_RWLockUnlock(&servPtr->tcl.lock);
2298
2299 if (itPtr->epoch != epoch) {
2300 if (doUpdateNow) {
2301 Ns_Time startTime, now, diffTime;
2302
2303 Ns_Log(Notice, "start update interpreter %s to epoch %d, concurrent %d",
2304 servPtr->server, epoch, concurrentUpdates);
2305 Ns_GetTime(&startTime);
2306 result = Tcl_EvalEx(itPtr->interp, script,
2307 scriptLength, TCL_EVAL_GLOBAL0x020000);
2308 Ns_GetTime(&now);
2309 Ns_DiffTime(&now, &startTime, &diffTime);
2310 Ns_Log(Notice, "update interpreter %s to epoch %d done, trace %s, time "
2311 NS_TIME_FMT"%" "l" "d" ".%06ld" " secs concurrent %d",
2312 servPtr->server, epoch,
2313 GetTraceLabel(itPtr->currentTrace),
2314 (int64_t) diffTime.sec, diffTime.usec,
2315 concurrentUpdates);
2316
2317 itPtr->epoch = epoch;
2318 ns_free((char *)script);
2319
2320 Ns_MutexLock(&updateLock);
2321 concurrentUpdates--;
2322 Ns_MutexUnlock(&updateLock);
2323 } else {
2324 Ns_Log(Notice, "postponed update, %s epoch %d interpreter (concurrent %d max %d)",
2325 servPtr->server, epoch, concurrentUpdates, maxConcurrentUpdates);
2326 }
2327 }
2328
2329 return result;
2330}
2331
2332
2333/*
2334 *----------------------------------------------------------------------
2335 *
2336 * RunTraces, LogTrace --
2337 *
2338 * Execute interp trace callbacks.
2339 *
2340 * Results:
2341 * None.
2342 *
2343 * Side effects:
2344 * Depends on callbacks. Event may be logged.
2345 *
2346 *----------------------------------------------------------------------
2347 */
2348static void
2349RunTraces(NsInterp *itPtr, Ns_TclTraceType why)
2350{
2351 const TclTrace *tracePtr;
2352 const NsServer *servPtr;
2353
2354 NS_NONNULL_ASSERT(itPtr != NULL)((void) (0));
2355
2356 servPtr = itPtr->servPtr;
2357 if (servPtr != NULL((void*)0)) {
2358
2359 //Ns_Log(Notice, "RunTraces %d", (int)why);
2360 itPtr->currentTrace = why;
2361
2362 switch (why) {
2363 case NS_TCL_TRACE_FREECONN: NS_FALL_THROUGH((void)0); /* fall through */
Duplicate code detected
2364 case NS_TCL_TRACE_DEALLOCATE: NS_FALL_THROUGH((void)0); /* fall through */
2365 case NS_TCL_TRACE_DELETE:
2366 /*
2367 * Run finalization traces in LIFO order.
2368 */
2369 tracePtr = servPtr->tcl.lastTracePtr;
2370 while (tracePtr != NULL((void*)0)) {
2371 if (tracePtr->when == why) {
2372 LogTrace(itPtr, tracePtr, why);
2373 if ((*tracePtr->proc)(itPtr->interp, tracePtr->arg) != TCL_OK0) {
2374 LogErrorInTrace(itPtr, "trace", why);
2375 }
2376 }
2377 tracePtr = tracePtr->prevPtr;
2378 }
2379 break;
2380
2381 case NS_TCL_TRACE_ALLOCATE: NS_FALL_THROUGH((void)0); /* fall through */
2382 case NS_TCL_TRACE_CREATE: NS_FALL_THROUGH((void)0); /* fall through */
Similar code here
2383 case NS_TCL_TRACE_GETCONN: NS_FALL_THROUGH((void)0); /* fall through */
2384 case NS_TCL_TRACE_IDLE:
2385 /*
2386 * Run initialization traces in FIFO order.
2387 */
2388 tracePtr = servPtr->tcl.firstTracePtr;
2389 while (tracePtr != NULL((void*)0)) {
2390 if (tracePtr->when == why) {
2391 LogTrace(itPtr, tracePtr, why);
2392 if ((*tracePtr->proc)(itPtr->interp, tracePtr->arg) != TCL_OK0) {
2393 LogErrorInTrace(itPtr, "trace", why);
2394 }
2395 }
2396 tracePtr = tracePtr->nextPtr;
2397 }
2398 break;
2399
2400 case NS_TCL_TRACE_NONE:
2401 break;
2402 }
2403 }
2404 itPtr->currentTrace = 0u;
2405}
2406
2407static void
2408LogTrace(const NsInterp *itPtr, const TclTrace *tracePtr, Ns_TclTraceType why)
2409{
2410 NS_NONNULL_ASSERT(itPtr != NULL)((void) (0));
2411 NS_NONNULL_ASSERT(tracePtr != NULL)((void) (0));
2412
2413 if (Ns_LogSeverityEnabled(Debug)) {
2414 Ns_DStringTcl_DString ds;
2415
2416 Ns_DStringInitTcl_DStringInit(&ds);
2417 Ns_DStringNAppendTcl_DStringAppend(&ds, GetTraceLabel(why), -1);
2418 Ns_DStringNAppendTcl_DStringAppend(&ds, " ", 1);
2419 Ns_GetProcInfo(&ds, (ns_funcptr_t)tracePtr->proc, tracePtr->arg);
2420 Ns_Log(Debug, "ns:interptrace[%s]: %s",
2421 itPtr->servPtr->server, Ns_DStringValue(&ds)((&ds)->string));
2422 Ns_DStringFreeTcl_DStringFree(&ds);
2423 }
2424}
2425
2426static void
2427LogErrorInTrace(const NsInterp *itPtr, const char *context, Ns_TclTraceType why) {
2428 Tcl_DString ds;
2429
2430 NS_NONNULL_ASSERT(itPtr != NULL)((void) (0));
2431
2432 Tcl_DStringInit(&ds);
2433 Ns_DStringPrintf(&ds, "\n(context: %s %s)", context, GetTraceLabel(why));
2434 (void) Ns_TclLogErrorInfo(itPtr->interp, ds.string);
2435 Tcl_DStringFree(&ds);
2436}
2437
2438
2439/*
2440 *----------------------------------------------------------------------
2441 *
2442 * FreeInterpData --
2443 *
2444 * Tcl assoc data callback to destroy the per-interp NsInterp
2445 * structure at interp delete time.
2446 *
2447 * Results:
2448 * None.
2449 *
2450 * Side effects:
2451 * None.
2452 *
2453 *----------------------------------------------------------------------
2454 */
2455
2456static void
2457FreeInterpData(ClientData clientData, Tcl_Interp *UNUSED(interp)UNUSED_interp __attribute__((__unused__)))
2458{
2459 NsInterp *itPtr = (NsInterp *)clientData;
2460
2461 NsAdpFree(itPtr);
2462 Tcl_DeleteHashTable(&itPtr->sets);
2463 Tcl_DeleteHashTable(&itPtr->chans);
2464 Tcl_DeleteHashTable(&itPtr->httpRequests);
2465
2466 ns_free(itPtr);
2467}
2468
2469
2470/*
2471 *----------------------------------------------------------------------
2472 *
2473 * DeleteInterps --
2474 *
2475 * Tls callback to delete all cache virtual-server interps at
2476 * thread exit time.
2477 *
2478 * Results:
2479 * None.
2480 *
2481 * Side effects:
2482 * None.
2483 *
2484 *----------------------------------------------------------------------
2485 */
2486
2487static void
2488DeleteInterps(void *arg)
2489{
2490 Tcl_HashTable *tablePtr = arg;
2491 const Tcl_HashEntry *hPtr;
2492 Tcl_HashSearch search;
2493
2494 hPtr = Tcl_FirstHashEntry(tablePtr, &search);
2495 while (hPtr != NULL((void*)0)) {
2496 const NsInterp *itPtr;
2497
2498 itPtr = Tcl_GetHashValue(hPtr)((hPtr)->clientData);
2499 if ((itPtr != NULL((void*)0)) && (itPtr->interp != NULL((void*)0))) {
2500 Ns_TclDestroyInterp(itPtr->interp);
2501 }
2502 hPtr = Tcl_NextHashEntry(&search);
2503 }
2504 Tcl_DeleteHashTable(tablePtr);
2505 ns_free(tablePtr);
2506}
2507
2508/*
2509 * Local Variables:
2510 * mode: c
2511 * c-basic-offset: 4
2512 * fill-column: 78
2513 * indent-tabs-mode: nil
2514 * End:
2515 */