Bug Summary

File:d/tclfile.c
Warning:line 279, column 31
Call to function 'mktemp' is insecure as it always creates or uses insecure temporary file. Use 'mkstemp' instead

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 tclfile.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 tclfile.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/*
32 * tclfile.c --
33 *
34 * Tcl commands that do stuff to the filesystem.
35 */
36
37#include "nsd.h"
38
39/*
40 * Static variables defined in this file.
41 */
42static Ns_ObjvValueRange posintRange0 = {0, INT_MAX2147483647};
43
44/*
45 * Structure handling one registered channel for the [ns_chan] command
46 */
47
48typedef struct {
49 const char *name;
50 Tcl_Channel chan;
51} NsRegChan;
52
53static void SpliceChannel(Tcl_Interp *interp, Tcl_Channel chan)
54 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2)));
55
56static void UnspliceChannel(Tcl_Interp *interp, Tcl_Channel chan)
57 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2)));
58
59static int FileObjCmd(Tcl_Interp *interp, int objc, Tcl_Obj *const* objv, const char *cmd)
60 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(4)__attribute__((__nonnull__(4)));
61
62static Tcl_ObjCmdProc ChanCleanupObjCmd;
63static Tcl_ObjCmdProc ChanListObjCmd;
64static Tcl_ObjCmdProc ChanCreateObjCmd;
65static Tcl_ObjCmdProc ChanPutObjCmd;
66static Tcl_ObjCmdProc ChanGetObjCmd;
67
68
69/*
70 *----------------------------------------------------------------------
71 *
72 * Ns_TclGetOpenChannel --
73 *
74 * Return an open channel with an interface similar to the
75 * pre-Tcl7.5 Tcl_GetOpenFile, used throughout the server.
76 *
77 * Results:
78 * TCL_OK or TCL_ERROR.
79 *
80 * Side effects:
81 * The value at chanPtr is updated with a valid open Tcl_Channel.
82 *
83 *----------------------------------------------------------------------
84 */
85
86int
87Ns_TclGetOpenChannel(Tcl_Interp *interp, const char *chanId, int write,
88 bool_Bool check, Tcl_Channel *chanPtr)
89{
90 int mode, result = TCL_OK0;
91
92 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
93 NS_NONNULL_ASSERT(chanId != NULL)((void) (0));
94 NS_NONNULL_ASSERT(chanPtr != NULL)((void) (0));
95
96 *chanPtr = Tcl_GetChannel(interp, chanId, &mode);
97
98 if (*chanPtr == NULL((void*)0)) {
99 result = TCL_ERROR1;
100 } else if (check) {
101
102 if (( write != 0 && (mode & TCL_WRITABLE(1<<2)) == 0)
103 ||
104 (write == 0 && (mode & TCL_READABLE(1<<1)) == 0)) {
105 Ns_TclPrintfResult(interp, "channel \"%s\" not open for %s",
106 chanId, write != 0 ? "writing" : "reading");
107 result = TCL_ERROR1;
108 }
109 }
110
111 return result;
112}
113
114
115/*
116 *----------------------------------------------------------------------
117 *
118 * Ns_TclGetOpenFd --
119 *
120 * Return an open Unix file descriptor for the given channel.
121 * This routine is used by the AOLserver * routines to provide
122 * access to the underlying socket.
123 *
124 * Results:
125 * TCL_OK or TCL_ERROR.
126 *
127 * Side effects:
128 * The value at fdPtr is updated with a valid Unix file descriptor.
129 *
130 *----------------------------------------------------------------------
131 */
132
133int
134Ns_TclGetOpenFd(Tcl_Interp *interp, const char *chanId, int write, int *fdPtr)
135{
136 Tcl_Channel chan;
137 ClientData data;
138 int result = TCL_OK0;
139
140 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
141 NS_NONNULL_ASSERT(chanId != NULL)((void) (0));
142 NS_NONNULL_ASSERT(fdPtr != NULL)((void) (0));
143
144 if (Ns_TclGetOpenChannel(interp, chanId, write, NS_TRUE1, &chan) != TCL_OK0) {
145 result = TCL_ERROR1;
146
147 } else if (Tcl_GetChannelHandle(chan, write != 0 ? TCL_WRITABLE(1<<2) : TCL_READABLE(1<<1),
148 &data) != TCL_OK0) {
149 Ns_TclPrintfResult(interp, "could not get handle for channel: %s", chanId);
150 result = TCL_ERROR1;
151
152 } else {
153 *fdPtr = PTR2INT(data)((int)(intptr_t)(data));
154 }
155
156 return result;
157}
158
159
160/*
161 *----------------------------------------------------------------------
162 *
163 * NsTclRollFileObjCmd --
164 *
165 * Implements "ns_rollfile".
166 *
167 * Results:
168 * Tcl result.
169 *
170 * Side effects:
171 * See docs.
172 *
173 *----------------------------------------------------------------------
174 */
175
176static int
177FileObjCmd(Tcl_Interp *interp, int objc, Tcl_Obj *const* objv, const char *cmd)
178{
179 int maxFiles = 0, result;
180 Tcl_Obj *fileObj = NULL((void*)0);
181 Ns_ObjvValueRange range = {0, 1000};
182
183 Ns_ObjvSpec args[] = {
184 {"path", Ns_ObjvObj, &fileObj, NULL((void*)0)},
185 {"maxbackups", Ns_ObjvInt, &maxFiles, &range},
186 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
187 };
188
189 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
190 NS_NONNULL_ASSERT(cmd != NULL)((void) (0));
191
192 if (Ns_ParseObjv(NULL((void*)0), args, interp, 1, objc, objv) != NS_OK) {
193 result = TCL_ERROR1;
194
195 } else {
196 /*
197 * All parameters are ok.
198 */
199 Ns_ReturnCode status;
200 const char *path = Tcl_GetString(fileObj);
201
202 if (*cmd == 'p' /* "purge" */ ) {
203 status = Ns_PurgeFiles(path, maxFiles);
204 } else /* must be "roll" */ {
205 status = Ns_RollFile(path, maxFiles);
206 }
207 if (status != NS_OK) {
208 Ns_TclPrintfResult(interp, "could not %s \"%s\": %s",
209 cmd, path, Tcl_PosixError(interp));
210 result = TCL_ERROR1;
211 } else {
212 result = TCL_OK0;
213 }
214 }
215
216 return result;
217}
218
219int
220NsTclRollFileObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
221{
222 return FileObjCmd(interp, objc, objv, "roll");
223}
224
225int
226NsTclPurgeFilesObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
227{
228 return FileObjCmd(interp, objc, objv, "purge");
229}
230
231
232/*
233 *----------------------------------------------------------------------
234 *
235 * NsTclMkTempObjCmd --
236 *
237 * Implements "ns_mktemp". The function generates a unique
238 * temporary filename using optionally a template as argument.
239 *
240 * In general, the function mktemp() is not recommended, since
241 * there is a time gap between the generation of a filename and
242 * the generation of a file or directory with the name. This can
243 * result in race conditions or attacks. However, using this
244 * function is still better than home-brewed solutions for the
245 * same task.
246 *
247 * Results:
248 * Tcl result.
249 *
250 * Side effects:
251 * Allocates potentially memory for the filename.
252 *
253 *----------------------------------------------------------------------
254 */
255int
256NsTclMkTempObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
257{
258 int result = TCL_OK0;
259 char *templateString = (char *)NS_EMPTY_STRING;
260 Ns_ObjvSpec args[] = {
261 {"?template", Ns_ObjvString, &templateString, NULL((void*)0)},
262 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
263 };
264
265 if (Ns_ParseObjv(NULL((void*)0), args, interp, 1, objc, objv) != NS_OK) {
266 result = TCL_ERROR1;
267
268 } else if (objc == 1) {
269 char buffer[PATH_MAX4096] = "";
270
271 snprintf(buffer, sizeof(buffer), "%s/ns-XXXXXX", nsconf.tmpDir)__builtin___snprintf_chk (buffer, sizeof(buffer), 2 - 1, __builtin_object_size
(buffer, 2 > 1), "%s/ns-XXXXXX", nsconf.tmpDir)
;
272 Tcl_SetObjResult(interp, Tcl_NewStringObj(mktemp(buffer), -1));
273
274 } else /*if (objc == 2)*/ {
275 char *buffer;
276
277 assert(templateString != NULL)((void) (0));
278 buffer = ns_strdup(templateString);
279 Tcl_SetResult(interp, mktemp(buffer), (Tcl_FreeProc *)ns_free);
Call to function 'mktemp' is insecure as it always creates or uses insecure temporary file. Use 'mkstemp' instead
280 }
281
282 return result;
283}
284
285
286/*
287 *----------------------------------------------------------------------
288 *
289 * NsTclKillObjCmd --
290 *
291 * Implements "ns_kill".
292 *
293 * Results:
294 * Tcl result.
295 *
296 * Side effects:
297 * See docs.
298 *
299 *----------------------------------------------------------------------
300 */
301int
302NsTclKillObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
303{
304 int pid = 0, sig = 0, nocomplain = (int)NS_FALSE0, result = TCL_OK0;
305 Ns_ObjvSpec opts[] = {
306 {"-nocomplain", Ns_ObjvBool, &nocomplain, INT2PTR(NS_TRUE)((void *)(intptr_t)(1))},
307 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
308 };
309 Ns_ObjvSpec args[] = {
310 {"pid", Ns_ObjvInt, &pid, NULL((void*)0)},
311 {"sig", Ns_ObjvInt, &sig, &posintRange0},
312 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
313 };
314
315 if (Ns_ParseObjv(opts, args, interp, 1, objc, objv) != NS_OK) {
316 result = TCL_ERROR1;
317
318 } else {
319 int rc = kill((pid_t)pid, sig);
320
321 if (rc != 0 && !nocomplain) {
322 Ns_TclPrintfResult(interp, "kill %d %d failed: %s", pid, sig, Tcl_PosixError(interp));
323 result = TCL_ERROR1;
324 }
325 }
326 return result;
327}
328
329
330/*
331 *----------------------------------------------------------------------
332 *
333 * NsTclSymlinkObjCmd --
334 *
335 * Implements "ns_symlink".
336 *
337 * Results:
338 * Tcl result.
339 *
340 * Side effects:
341 * See docs.
342 *
343 *----------------------------------------------------------------------
344 */
345int
346NsTclSymlinkObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
347{
348 char *file1, *file2;
349 int nocomplain = (int)NS_FALSE0, result;
350 Ns_ObjvSpec opts[] = {
351 {"-nocomplain", Ns_ObjvBool, &nocomplain, INT2PTR(NS_TRUE)((void *)(intptr_t)(1))},
352 {"--", Ns_ObjvBreak, NULL((void*)0), NULL((void*)0)},
353 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
354 };
355 Ns_ObjvSpec args[] = {
356 {"file1", Ns_ObjvString, &file1, NULL((void*)0)},
357 {"file2", Ns_ObjvString, &file2, NULL((void*)0)},
358 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
359 };
360
361 if (Ns_ParseObjv(opts, args, interp, 1, objc, objv) != NS_OK) {
362 result = TCL_ERROR1;
363
364 } else {
365 int rc = result = symlink(file1, file2);
366 if (rc != 0 && !nocomplain) {
367 Ns_TclPrintfResult(interp, "symlink '%s' '%s' failed: %s", file1, file2,
368 Tcl_PosixError(interp));
369 result = TCL_ERROR1;
370 }
371 }
372
373 return result;
374}
375
376
377/*
378 *----------------------------------------------------------------------
379 *
380 * NsTclWriteFpObjCmd --
381 *
382 * Implements "ns_writefp".
383 *
384 * Results:
385 * Tcl result.
386 *
387 * Side effects:
388 * See docs.
389 *
390 *----------------------------------------------------------------------
391 */
392int
393NsTclWriteFpObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
394{
395 Tcl_Channel chan = NULL((void*)0);
396 Tcl_WideInt nbytes = -1;
397 int result = TCL_OK0;
398
399 if (unlikely(objc < 2 || objc > 3)(__builtin_expect((objc < 2 || objc > 3), 0))) {
400 Tcl_WrongNumArgs(interp, 1, objv, "fileid ?nbytes?");
401 result = TCL_ERROR1;
402
403 } else if (/*objc >= 2*/
404 Ns_TclGetOpenChannel(interp, Tcl_GetString(objv[1]),
405 0, NS_TRUE1, &chan) != TCL_OK0) {
406 result = TCL_ERROR1;
407 } else if (objc == 3
408 && Tcl_GetWideIntFromObj(interp, objv[2], &nbytes) != TCL_OK0) {
409
410 result = TCL_ERROR1;
411 } else if (NsConnRequire(interp, NS_CONN_REQUIRE_ALL0x0007u, NULL((void*)0)) != NS_OK) {
412 result = TCL_ERROR1;
413
414 } else {
415
416 /*
417 * All parameters are ok.
418 */
419
420 const NsInterp *itPtr = clientData;
421 Ns_ReturnCode status;
422
423 status = Ns_ConnSendChannel(itPtr->conn, chan, (ssize_t)nbytes);
424
425 if (unlikely(status != NS_OK)(__builtin_expect((status != NS_OK), 0))) {
426 Ns_TclPrintfResult(interp, "I/O failed");
427 result = TCL_ERROR1;
428 }
429 }
430
431 return result;
432}
433
434
435/*
436 *----------------------------------------------------------------------
437 *
438 * NsTclTruncateObjCmd --
439 *
440 * Implements "ns_truncate".
441 *
442 * Results:
443 * Tcl result.
444 *
445 * Side effects:
446 * See docs.
447 *
448 *----------------------------------------------------------------------
449 */
450int
451NsTclTruncateObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
452{
453 char *fileString;
454 int length = 0, result = TCL_OK0;
455 Ns_ObjvSpec args[] = {
456 {"file", Ns_ObjvString, &fileString, NULL((void*)0)},
457 {"?length", Ns_ObjvInt, &length, &posintRange0},
458 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
459 };
460
461 if (Ns_ParseObjv(NULL((void*)0), args, interp, 1, objc, objv) != NS_OK) {
462 result = TCL_ERROR1;
463
464 } else if (truncate(fileString, (off_t)length) != 0) {
465 Ns_TclPrintfResult(interp, "truncate (\"%s\", %s) failed: %s",
466 fileString,
467 length == 0 ? "0" : Tcl_GetString(objv[2]),
468 Tcl_PosixError(interp));
469 result = TCL_ERROR1;
470 }
471
472 return result;
473}
474
475
476/*
477 *----------------------------------------------------------------------
478 *
479 * NsTclFTruncateObjCmd --
480 *
481 * Implements "ns_ftruncate".
482 *
483 * Results:
484 * Tcl result.
485 *
486 * Side effects:
487 * See docs.
488 *
489 *----------------------------------------------------------------------
490 */
491int
492NsTclFTruncateObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
493{
494 int fd, length = 0, result = TCL_OK0;
495 char *fileIdString;
496 Ns_ObjvSpec args[] = {
497 {"fileId", Ns_ObjvString, &fileIdString, NULL((void*)0)},
498 {"?length", Ns_ObjvInt, &length, &posintRange0},
499 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
500 };
501
502 if (Ns_ParseObjv(NULL((void*)0), args, interp, 1, objc, objv) != NS_OK) {
503 result = TCL_ERROR1;
504
505 } else if (Ns_TclGetOpenFd(interp, fileIdString, 1, &fd) != TCL_OK0) {
506 result = TCL_ERROR1;
507
508 } else if (ftruncate(fd, (off_t)length) != 0) {
509 Ns_TclPrintfResult(interp, "ftruncate (\"%s\", %s) failed: %s",
510 fileIdString,
511 length == 0 ? "0" : Tcl_GetString(objv[2]),
512 Tcl_PosixError(interp));
513 result = TCL_ERROR1;
514 }
515
516 return result;
517}
518
519/*
520 *----------------------------------------------------------------------
521 *
522 * NsTclNormalizePathObjCmd --
523 *
524 * Implements "ns_normalizepath".
525 *
526 * Results:
527 * Tcl result.
528 *
529 * Side effects:
530 * See docs.
531 *
532 *----------------------------------------------------------------------
533 */
534int
535NsTclNormalizePathObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
536{
537 Ns_DStringTcl_DString ds;
538 int result = TCL_OK0;
539
540 if (objc != 2) {
541 Tcl_WrongNumArgs(interp, 1, objv, "path");
542 result = TCL_ERROR1;
543
544 } else {
545 Ns_DStringInitTcl_DStringInit(&ds);
546 Ns_NormalizePath(&ds, Tcl_GetString(objv[1]));
547 Tcl_DStringResult(interp, &ds);
548 }
549
550 return result;
551}
552
553
554/*
555 *----------------------------------------------------------------------
556 *
557 * ChanCreateObjCmd --
558 *
559 * Implements "ns_chan create".
560 *
561 * Results:
562 * Tcl result.
563 *
564 * Side effects:
565 * See docs.
566 *
567 *----------------------------------------------------------------------
568 */
569static int
570ChanCreateObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
571{
572 char *name, *chanName;
573 int result = TCL_OK0;
574 Ns_ObjvSpec args[] = {
575 {"channel", Ns_ObjvString, &chanName, NULL((void*)0)},
576 {"name", Ns_ObjvString, &name, NULL((void*)0)},
577 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
578 };
579
580 if (Ns_ParseObjv(NULL((void*)0), args, interp, 2, objc, objv) != NS_OK) {
581 result = TCL_ERROR1;
582 } else {
583 Tcl_Channel chan;
584
585 chan = Tcl_GetChannel(interp, chanName, NULL((void*)0));
586 if (chan == (Tcl_Channel)NULL((void*)0)) {
587 result = TCL_ERROR1;
588
589 } else if (Tcl_IsChannelShared(chan) == 1) {
590 Ns_TclPrintfResult(interp, "channel is shared");
591 result = TCL_ERROR1;
592
593 } else {
594 /*
595 * All parameters are ok.
596 */
597 const NsInterp *itPtr = clientData;
598 NsServer *servPtr = itPtr->servPtr;
599 Tcl_HashEntry *hPtr;
600 int isNew;
601
602 Ns_MutexLock(&servPtr->chans.lock);
603 hPtr = Tcl_CreateHashEntry(&servPtr->chans.table, name, &isNew)(*((&servPtr->chans.table)->createProc))(&servPtr
->chans.table, (const char *)(name), &isNew)
;
604 if (isNew != 0) {
605 NsRegChan *regChan;
606
607 /*
608 * Allocate a new NsRegChan entry.
609 */
610 regChan = ns_malloc(sizeof(NsRegChan));
611 regChan->name = ns_strdup(chanName);
612 regChan->chan = chan;
613 Tcl_SetHashValue(hPtr, regChan)((hPtr)->clientData = (ClientData) (regChan));
614 }
615 Ns_MutexUnlock(&servPtr->chans.lock);
616 if (isNew == 0) {
617 Ns_TclPrintfResult(interp, "channel \"%s\" already exists",
618 name);
619 result = TCL_ERROR1;
620 } else {
621 UnspliceChannel(interp, chan);
622 }
623 }
624 }
625 return result;
626}
627
628
629/*
630 *----------------------------------------------------------------------
631 *
632 * ChanGetObjCmd --
633 *
634 * Implements "ns_chan get".
635 *
636 * Results:
637 * Tcl result.
638 *
639 * Side effects:
640 * See docs.
641 *
642 *----------------------------------------------------------------------
643 */
644static int
645ChanGetObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
646{
647 char *name;
648 int result = TCL_OK0;
649 Ns_ObjvSpec args[] = {
650 {"name", Ns_ObjvString, &name, NULL((void*)0)},
651 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
652 };
653
654 if (Ns_ParseObjv(NULL((void*)0), args, interp, 2, objc, objv) != NS_OK) {
655 result = TCL_ERROR1;
656 } else {
657 Tcl_HashEntry *hPtr;
658 NsInterp *itPtr = clientData;
659 NsServer *servPtr = itPtr->servPtr;
660 NsRegChan *regChan = NULL((void*)0);
661
662 Ns_MutexLock(&servPtr->chans.lock);
663 hPtr = Tcl_FindHashEntry(&servPtr->chans.table, name)(*((&servPtr->chans.table)->findProc))(&servPtr
->chans.table, (const char *)(name))
;
664 if (hPtr != NULL((void*)0)) {
665 regChan = (NsRegChan*)Tcl_GetHashValue(hPtr)((hPtr)->clientData);
666 Tcl_DeleteHashEntry(hPtr);
667 assert(regChan != NULL)((void) (0));
668 }
669 Ns_MutexUnlock(&servPtr->chans.lock);
670
671 if (hPtr == NULL((void*)0)) {
672 Ns_TclPrintfResult(interp, "channel \"%s\" not found", name);
673 result = TCL_ERROR1;
674 } else {
675 int isNew;
676
677 /*
678 * We have a valid NsRegChan.
679 */
680 assert(regChan != NULL)((void) (0));
681 SpliceChannel(interp, regChan->chan);
682 Tcl_SetObjResult(interp, Tcl_NewStringObj(regChan->name, -1));
683 hPtr = Tcl_CreateHashEntry(&itPtr->chans, name, &isNew)(*((&itPtr->chans)->createProc))(&itPtr->chans
, (const char *)(name), &isNew)
;
684 Tcl_SetHashValue(hPtr, regChan)((hPtr)->clientData = (ClientData) (regChan));
685 }
686 }
687 return result;
688}
689
690
691/*
692 *----------------------------------------------------------------------
693 *
694 * ChanPutObjCmd --
695 *
696 * Implements "ns_chan put".
697 *
698 * Results:
699 * Tcl result.
700 *
701 * Side effects:
702 * See docs.
703 *
704 *----------------------------------------------------------------------
705 */
706static int
707ChanPutObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
708{
709 char *name;
710 int result = TCL_OK0;
711 Ns_ObjvSpec args[] = {
712 {"name", Ns_ObjvString, &name, NULL((void*)0)},
713 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
714 };
715
716 if (Ns_ParseObjv(NULL((void*)0), args, interp, 2, objc, objv) != NS_OK) {
717 result = TCL_ERROR1;
718 } else {
719 NsInterp *itPtr = clientData;
720 Tcl_HashEntry *hPtr = Tcl_FindHashEntry(&itPtr->chans, name)(*((&itPtr->chans)->findProc))(&itPtr->chans
, (const char *)(name))
;
721
722 if (hPtr == NULL((void*)0)) {
723 Ns_TclPrintfResult(interp, "channel \"%s\" not found", name);
724 result = TCL_ERROR1;
725
726 } else {
727 NsRegChan *regChan;
728 Tcl_Channel chan;
729
730 regChan = (NsRegChan*)Tcl_GetHashValue(hPtr)((hPtr)->clientData);
731 chan = Tcl_GetChannel(interp, regChan->name, NULL((void*)0));
732 if (chan == NULL((void*)0) || chan != regChan->chan) {
733 Tcl_DeleteHashEntry(hPtr);
734 if (chan != regChan->chan) {
735 Ns_TclPrintfResult(interp, "channel mismatch");
736 }
737 result = TCL_ERROR1;
738 } else {
739 NsServer *servPtr = itPtr->servPtr;
740 int isNew;
741
742 /*
743 * We have a valid NsRegChan.
744 */
745 UnspliceChannel(interp, regChan->chan);
746 Tcl_DeleteHashEntry(hPtr);
747 Ns_MutexLock(&servPtr->chans.lock);
748 hPtr = Tcl_CreateHashEntry(&servPtr->chans.table, name, &isNew)(*((&servPtr->chans.table)->createProc))(&servPtr
->chans.table, (const char *)(name), &isNew)
;
749 Tcl_SetHashValue(hPtr, regChan)((hPtr)->clientData = (ClientData) (regChan));
750 Ns_MutexUnlock(&servPtr->chans.lock);
751 }
752 }
753 }
754 return result;
755}
756
757
758/*
759 *----------------------------------------------------------------------
760 *
761 * ChanListObjCmd --
762 *
763 * Implements "ns_chan list".
764 *
765 * Results:
766 * Tcl result.
767 *
768 * Side effects:
769 * See docs.
770 *
771 *----------------------------------------------------------------------
772 */
773static int
774ChanListObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
775{
776 int result = TCL_OK0, isShared = (int)NS_FALSE0;
777 Ns_ObjvSpec lopts[] = {
778 {"-shared", Ns_ObjvBool, &isShared, INT2PTR(NS_TRUE)((void *)(intptr_t)(1))},
779 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
780 };
781
782 if (Ns_ParseObjv(lopts, NULL((void*)0), interp, 2, objc, objv) != NS_OK) {
783 result = TCL_ERROR1;
784 } else {
785 const Tcl_HashEntry *hPtr;
786 Tcl_HashSearch search;
787 NsInterp *itPtr = clientData;
788 NsServer *servPtr = itPtr->servPtr;
789 Tcl_HashTable *tabPtr;
790 Tcl_Obj *listObj = Tcl_NewListObj(0, NULL((void*)0));
791
792 if (isShared != (int)NS_FALSE0) {
793 Ns_MutexLock(&servPtr->chans.lock);
794 tabPtr = &servPtr->chans.table;
795 } else {
796 tabPtr = &itPtr->chans;
797 }
798
799 /*
800 * Compute a Tcl list of the keys of every entry of the hash
801 * table.
802 */
803 for (hPtr = Tcl_FirstHashEntry(tabPtr, &search); hPtr != NULL((void*)0);
804 hPtr = Tcl_NextHashEntry(&search)) {
805 const char *key = Tcl_GetHashKey(tabPtr, hPtr)((void *) (((tabPtr)->keyType == (1) || (tabPtr)->keyType
== (-1)) ? (hPtr)->key.oneWordValue : (hPtr)->key.string
))
;
806 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj(key, -1));
807 }
808 if (isShared != (int)NS_FALSE0) {
809 Ns_MutexUnlock(&servPtr->chans.lock);
810 }
811 Tcl_SetObjResult(interp, listObj);
812
813 }
814 return result;
815}
816
817
818/*
819 *----------------------------------------------------------------------
820 *
821 * ChanCleanupObjCmd --
822 *
823 * Implements "ns_chan cleanup".
824 *
825 * Results:
826 * Tcl result.
827 *
828 * Side effects:
829 * See docs.
830 *
831 *----------------------------------------------------------------------
832 */
833static int
834ChanCleanupObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
835{
836 int result = TCL_OK0, isShared = (int)NS_FALSE0;
837 Ns_ObjvSpec lopts[] = {
838 {"-shared", Ns_ObjvBool, &isShared, INT2PTR(NS_TRUE)((void *)(intptr_t)(1))},
839 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
840 };
841
842 if (Ns_ParseObjv(lopts, NULL((void*)0), interp, 2, objc, objv) != NS_OK) {
843 result = TCL_ERROR1;
844 } else {
845 NsInterp *itPtr = clientData;
846 NsServer *servPtr = itPtr->servPtr;
847 Tcl_HashTable *tabPtr;
848 Tcl_HashEntry *hPtr;
849 Tcl_HashSearch search;
850
851 if (isShared != (int)NS_FALSE0) {
852 Ns_MutexLock(&servPtr->chans.lock);
853 tabPtr = &servPtr->chans.table;
854 } else {
855 tabPtr = &itPtr->chans;
856 }
857
858 /*
859 * Cleanup every entry found in the hash table.
860 */
861 for (hPtr = Tcl_FirstHashEntry(tabPtr, &search); hPtr != NULL((void*)0); hPtr = Tcl_NextHashEntry(&search)) {
862 NsRegChan *regChan;
863
864 regChan = (NsRegChan*)Tcl_GetHashValue(hPtr)((hPtr)->clientData);
865 assert(regChan != NULL)((void) (0));
866 if (isShared != (int)NS_FALSE0) {
867 Tcl_SpliceChannel(regChan->chan);
868 (void) Tcl_UnregisterChannel((Tcl_Interp*)NULL((void*)0), regChan->chan);
869 } else {
870 (void) Tcl_UnregisterChannel(interp, regChan->chan);
871 }
872 ns_free((char *)regChan->name);
873 ns_free(regChan);
874 Tcl_DeleteHashEntry(hPtr);
875 }
876 if (isShared != (int)NS_FALSE0) {
877 Ns_MutexUnlock(&servPtr->chans.lock);
878 }
879 }
880 return result;
881}
882
883
884/*
885 *----------------------------------------------------------------------
886 *
887 * NsTclChanObjCmd --
888 *
889 * Implements "ns_chan".
890 *
891 * Results:
892 * Tcl result.
893 *
894 * Side effects:
895 * See docs.
896 *
897 *----------------------------------------------------------------------
898 */
899
900int
901NsTclChanObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
902{
903 const Ns_SubCmdSpec subcmds[] = {
904 {"cleanup", ChanCleanupObjCmd},
905 {"list", ChanListObjCmd},
906 {"create", ChanCreateObjCmd},
907 {"put", ChanPutObjCmd},
908 {"get", ChanGetObjCmd},
909 {NULL((void*)0), NULL((void*)0)}
910 };
911
912 return Ns_SubcmdObjv(subcmds, clientData, interp, objc, objv);
913}
914
915
916/*
917 *----------------------------------------------------------------------
918 *
919 * SpliceChannel
920 *
921 * Adds the shared channel in the interp/thread.
922 *
923 * Results:
924 * None.
925 *
926 * Side effects:
927 * New channel appears in the interp.
928 *
929 *----------------------------------------------------------------------
930 */
931
932static void
933SpliceChannel(Tcl_Interp *interp, Tcl_Channel chan)
934{
935 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
936 NS_NONNULL_ASSERT(chan != NULL)((void) (0));
937
938 Tcl_SpliceChannel(chan);
939 Tcl_RegisterChannel(interp, chan);
940 (void) Tcl_UnregisterChannel((Tcl_Interp*)NULL((void*)0), chan);
941}
942
943
944/*
945 *----------------------------------------------------------------------
946 *
947 * UnspliceChannel
948 *
949 * Divorces the channel from its owning interp/thread.
950 *
951 * Results:
952 * None.
953 *
954 * Side effects:
955 * Channel is not accessible by Tcl scripts any more.
956 *
957 *----------------------------------------------------------------------
958 */
959
960static void
961UnspliceChannel(Tcl_Interp *interp, Tcl_Channel chan)
962{
963 const Tcl_ChannelType *chanTypePtr;
964 Tcl_DriverWatchProc *watchProc;
965
966 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
967 NS_NONNULL_ASSERT(chan != NULL)((void) (0));
968
969 Tcl_ClearChannelHandlers(chan);
970
971 chanTypePtr = Tcl_GetChannelType(chan);
972 watchProc = Tcl_ChannelWatchProc(chanTypePtr);
973
974 /*
975 * This effectively disables processing of pending
976 * events which are ready to fire for the given
977 * channel. If we do not do this, events will hit
978 * the detached channel which is potentially being
979 * owned by some other thread. This will wreck havoc
980 * on our memory and eventually badly hurt us...
981 */
982
983 if (watchProc != NULL((void*)0)) {
984 (*watchProc)(Tcl_GetChannelInstanceData(chan), 0);
985 }
986
987 /*
988 * Artificially bump the channel reference count
989 * which protects us from channel being closed
990 * during the Tcl_UnregisterChannel().
991 */
992
993 Tcl_RegisterChannel(NULL((void*)0), chan);
994 (void) Tcl_UnregisterChannel(interp, chan);
995
996 Tcl_CutChannel(chan);
997}
998
999/*
1000 * Local Variables:
1001 * mode: c
1002 * c-basic-offset: 4
1003 * fill-column: 70
1004 * indent-tabs-mode: nil
1005 * End:
1006 */