Bug Summary

File:proxy/nsproxylib.c
Warning:line 1760, column 1
Duplicate code detected
Note:line 1819, column 1
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 nsproxylib.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/nsproxy -resource-dir /usr/local/lib/clang/15.0.0 -D NSPROXY_HELPER="nsproxy-helper" -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/nsproxy -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 nsproxylib.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://www.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 * nsproxylib.c --
32 *
33 * Library for ns_proxy commands and main loops.
34 *
35 * TODO:
36 *
37 * Expand the Req structure to pass:
38 *
39 * o. Array of limits as with get/setrlimit
40 * o. Chroot of the worker process
41 * o. Limit duration of the execution in the worker process
42 * o. ...
43 *
44 * Add -onexit for worker process to run on teardown
45 * Add channels to proxy, so we can talk to it
46 */
47
48#include "nsproxy.h"
49
50static const char * NS_EMPTY_STRING = "";
51
52#ifdef _WIN32
53# define SIGKILL9 9
54# define SIGTERM15 15
55
56ssize_t writev(int fildes, const struct iovec *iov, int iovcnt);
57
58/*
59 * Minimal writev() and readv() emulation for windows. Must be probably
60 * extended to be useful.
61 */
62ssize_t writev(int fildes, const struct iovec *iov, int iovcnt)
63{
64 ssize_t result = 0;
65 int i;
66
67 for (i = 0; i < iovcnt; i++) {
68 ssize_t written = ns_writewrite(fildes, iov[i].iov_base, iov[i].iov_len);
69
70 if (written != iov[i].iov_len) {
71 /*
72 * Give up, since we did not receive the expected data.
73 * Maybe overly cautious and we have to handle partial
74 * writes.
75 */
76 result = -1;
77 break;
78 } else {
79 result += written;
80 }
81 }
82
83 return result;
84}
85
86ssize_t readv(int fildes, const struct iovec *iov, int iovcnt)
87{
88 ssize_t result = 0;
89 int i;
90
91 for (i = 0; i < iovcnt; i++) {
92 ssize_t read = ns_readread(fildes, iov[i].iov_base, iov[i].iov_len);
93
94 if (read < 0) {
95 result = -1;
96 break;
97 } else {
98 result += read;
99 }
100 }
101
102 return result;
103}
104#else
105# include <grp.h>
106# include <poll.h>
107#endif
108
109/*
110 * It is pain in the neck to get a satisfactory definition of
111 * u_int_XX_t or uintXX_t as different OS'es do that in different
112 * header files and sometimes even do not define such types at all.
113 * We choose to define them ourselves here and stop the blues.
114 * This relies on the assumption that on both 32 and 64 bit machines
115 * an int is always 32 and short is always 16 bits.
116 */
117
118typedef unsigned int uint32;
119typedef unsigned short uint16;
120
121#define MAJOR_VERSION1 1
122#define MINOR_VERSION1 1
123
124/*
125 * The following structure defines a running proxy worker process.
126 */
127
128typedef struct Worker {
129 int rfd;
130 int wfd;
131 int signal;
132 int sigsent;
133 int twait;
134 pid_t pid;
135 Ns_Time expire;
136 struct Pool *poolPtr;
137 struct Worker *nextPtr;
138} Worker;
139
140/*
141 * The following structures defines a proxy request and response.
142 * The lengths are in network order to support later proxy
143 * operation over a socket connection.
144 */
145
146typedef struct Req {
147 uint32 len; /* Length of the message */
148 uint16 major; /* Major version number */
149 uint16 minor; /* Minor version number */
150} Req;
151
152typedef struct Res {
153 uint32 code;
154 uint32 clen;
155 uint32 ilen;
156 uint32 rlen;
157} Res;
158
159/*
160 * The following structure defines a proxy connection allocated
161 * from a pool.
162 */
163
164typedef enum {
165 Idle, /* Ready to receive a script */
166 Busy, /* Evaluating a script */
167 Done /* Result is pending */
168} ProxyState;
169
170typedef struct ProxyConf {
171 Ns_Time tget; /* Timeout when getting proxy handles */
172 Ns_Time teval; /* Timeout when evaluating scripts */
173 Ns_Time tsend; /* Timeout to send data to proxy over pipe */
174 Ns_Time trecv; /* Timeout to receive results over pipe */
175 Ns_Time twait; /* Timeout to wait for workers to die */
176 Ns_Time tidle; /* Timeout for worker to be idle */
177 Ns_Time logminduration; /* Log commands taking longer than this duration */
178 int maxruns; /* Max number of proxy uses */
179} ProxyConf;
180
181typedef struct Proxy {
182 struct Proxy *nextPtr; /* Next in list of proxies */
183 struct Proxy *runPtr; /* Next in list of running proxies */
184 struct Pool *poolPtr; /* Pointer to proxy's pool */
185 char *id; /* Proxy unique string id */
186 int numruns; /* Number of runs of this proxy */
187 ProxyState state; /* Current proxy state (idle, busy etc) */
188 ProxyConf conf; /* Copy from the pool configuration */
189 Worker *workerPtr; /* Running worker process, if any */
190 Ns_Time when; /* Absolute time when the proxy is used */
191 Tcl_HashEntry *idPtr; /* Pointer to proxy table entry */
192 Tcl_HashEntry *cntPtr; /* Pointer to count of proxies allocated */
193 Tcl_DString in; /* Request dstring */
194 Tcl_DString out; /* Response dstring */
195 Tcl_Command cmdToken; /* Proxy Tcl command */
196 Tcl_Interp *interp; /* Interp holding the proxy's Tcl command */
197} Proxy;
198
199/*
200 * The following structure defines a proxy pool.
201 */
202
203typedef enum {
204 Stopped, /* Initial (startup) state */
205 Starting, /* It is in the process of startup */
206 Running, /* Operating on pools and tearing down workers */
207 Sleeping, /* Sleeping on cond var and waiting for work */
208 Awaken, /* Help state to distinguish from running */
209 Stopping /* Teardown of the thread initiated */
210} ReaperState;
211
212typedef struct Pool {
213 const char *name; /* Name of pool */
214 struct Proxy *firstPtr; /* First in list of avail proxies */
215 struct Proxy *runPtr; /* First in list of running proxies */
216 const char *exec; /* Worker executable */
217 const char *init; /* Init script to eval on proxy start */
218 const char *reinit; /* Re-init scripts to eval on proxy put */
219 int waiting; /* Thread waiting for handles */
220 int maxworker; /* Max number of allowed worker processes */
221 int nfree; /* Current number of available proxy handles */
222 int nused; /* Current number of used proxy handles */
223 uintptr_t nextid; /* Next in proxy unique ids; corresponds to nr of workers */
224 ProxyConf conf; /* Collection of config options to pass to proxy */
225 Ns_Set *env; /* Set with environment to pass to proxy */
226 Ns_Mutex lock; /* Lock around the pool */
227 Ns_Cond cond; /* Cond for use while allocating handles */
228 Ns_Time runTime; /* cumulated run times */
229 uintptr_t nruns; /* number of runs in this pool */
230} Pool;
231
232#define MIN_IDLE_TIMEOUT_SEC10 10 /* == 10 seconds */
233
234/*
235 * The following enum lists all possible error conditions.
236 */
237
238typedef enum Err {
239 ENone,
240 EBusy,
241 EDead,
242 EDeadlock,
243 EExec,
244 EGetTimeout,
245 EIdle,
246 EImport,
247 EInit,
248 ERange,
249 ERecv,
250 ESend,
251 ENoWait,
252 EEvalTimeout
253} Err;
254
255static const char *errMsg[] = {
256 "no error",
257 "currently evaluating a script",
258 "child process died",
259 "allocation deadlock",
260 "could not create child process",
261 "timeout waiting for handle",
262 "no script evaluating",
263 "invalid response",
264 "init script failed",
265 "insufficient handles",
266 "result recv failed",
267 "script send failed",
268 "no wait for script result",
269 "timeout waiting for evaluation",
270 NULL((void*)0)
271};
272
273static const char *errCode[] = {
274 "ENone",
275 "EBusy",
276 "EDead",
277 "EDeadlock",
278 "EExec",
279 "EGetTimeout",
280 "EIdle",
281 "EImport",
282 "EInit",
283 "ERange",
284 "ERecv",
285 "ESend",
286 "ENoWait",
287 "EEvalTimeout",
288 NULL((void*)0)
289};
290
291static Ns_LogSeverity Ns_LogNsProxyDebug = 0;
292
293
294/*
295 * Static functions defined in this file.
296 */
297
298static Tcl_ObjCmdProc ProxyObjCmd;
299static Tcl_ObjCmdProc ConfigureObjCmd;
300static Tcl_ObjCmdProc GetObjCmd;
301static Tcl_ObjCmdProc StatsObjCmd;
302static Tcl_ObjCmdProc ClearObjCmd;
303static Tcl_ObjCmdProc StopObjCmd;
304
305static Tcl_ObjCmdProc RunProxyObjCmd;
306static Tcl_CmdDeleteProc DelProxyProc;
307static Tcl_InterpDeleteProc DeleteData;
308
309static Ns_ShutdownProc Shutdown;
310
311static Pool* GetPool(const char *poolName, const InterpData *idataPtr) NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
312static void FreePool(Pool *poolPtr) NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
313
314static Proxy* CreateProxy(Pool *poolPtr) NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
315static Err PopProxy(Pool *poolPtr, Proxy **proxyPtrPtr, int nwant, const Ns_Time *timePtr)
316 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2)));
317static void PushProxy(Proxy *proxyPtr) NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
318static Proxy* GetProxy(const char *proxyId, InterpData *idataPtr) NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2)));
319
320static int Eval(Tcl_Interp *interp, Proxy *proxyPtr, const char *script, const Ns_Time *timeoutPtr)
321 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2)));
322
323static Err Send(Tcl_Interp *interp, Proxy *proxyPtr, const char *script)
324 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2)));
325static Err Wait(Tcl_Interp *interp, Proxy *proxyPtr, const Ns_Time *timeoutPtr)
326 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2)));
327static Err Recv(Tcl_Interp *interp, Proxy *proxyPtr, int *resultPtr)
328 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2))) NS_GNUC_NONNULL(3)__attribute__((__nonnull__(3)));
329
330static void GetStats(const Proxy *proxyPtr) NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
331
332static Err CheckProxy(Tcl_Interp *interp, Proxy *proxyPtr) NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2)));
333static int ReleaseProxy(Tcl_Interp *interp, Proxy *proxyPtr) NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2)));
334static void CloseProxy(Proxy *proxyPtr) NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
335static int CloseWorkerOfProxy(Proxy *proxyPtr, const char *proxyId, const Ns_Time *timePtr)
336 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
337
338static void FreeProxy(Proxy *proxyPtr) NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
339static void ResetProxy(Proxy *proxyPtr) NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
340static void ProxyError(Tcl_Interp *interp, Err err) NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
341static void FmtActiveProxy(Tcl_Interp *interp, const Proxy *proxyPtr)
342 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2)));
343
344static void ReleaseHandles(Tcl_Interp *interp, InterpData *idataPtr)
345 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2)));
346static Worker* ExecWorker(Tcl_Interp *interp, const Proxy *proxyPtr)
347 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2)));
348static Err CreateWorker(Tcl_Interp *interp, Proxy *proxyPtr)
349 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2)));
350
351static void SetExpire(Worker *workerPtr, const Ns_Time *timePtr)
352 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
353static bool_Bool SendBuf(const Worker *workerPtr, const Ns_Time *timePtr, const Tcl_DString *dsPtr)
354 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(3)__attribute__((__nonnull__(3)));
355static bool_Bool RecvBuf(const Worker *workerPtr, const Ns_Time *timePtr, Tcl_DString *dsPtr)
356 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(3)__attribute__((__nonnull__(3)));
357static bool_Bool WaitFd(int fd, short events, long ms);
358
359static int Import(Tcl_Interp *interp, const Tcl_DString *dsPtr, int *resultPtr)
360 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2))) NS_GNUC_NONNULL(3)__attribute__((__nonnull__(3)));
361static void Export(Tcl_Interp *interp, int code, Tcl_DString *dsPtr)
362 NS_GNUC_NONNULL(3)__attribute__((__nonnull__(3)));
363
364static void UpdateIov(struct iovec *iov, size_t n)
365 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
366static void SetOpt(const char *str, char const **optPtr)
367 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2)));
368static void ReaperThread(void *UNUSED(arg)UNUSED_arg __attribute__((__unused__)));
369static void CloseWorker(Worker *workerPtr, const Ns_Time *timePtr)
370 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
371static void ReapProxies(void);
372static long GetTimeDiff(const Ns_Time *timePtr)
373 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
374
375static void AppendObj(Tcl_Obj *listObj, const char *flag, Tcl_Obj *obj)
376 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2)));
377static Tcl_Obj* StringObj(const char* chars);
378
379/*
380 * Static variables defined in this file.
381 */
382
383static Tcl_HashTable pools; /* Tracks proxy pools */
384
385static ReaperState reaperState = Stopped;
386
387static Ns_Cond pcond = NULL((void*)0); /* Those are used to control access to */
388static Ns_Mutex plock = NULL((void*)0); /* The list of Worker structures of worker */
389static Worker *firstClosePtr = NULL((void*)0); /* Processes which are being closed. */
390
391static Tcl_DString defexec; /* Stores full path of the proxy executable */
392
393
394/*
395 *----------------------------------------------------------------------
396 *
397 * Nsproxy_Init --
398 *
399 * libnsproxy initialization.
400 *
401 * Results:
402 * None.
403 *
404 * Side effects:
405 * None.
406 *
407 *----------------------------------------------------------------------
408 */
409#define stringify(s)"s" #s
410void
411Nsproxy_LibInit(void)
412{
413 static bool_Bool initialized = NS_FALSE0;
414
415 if (!initialized) {
416 initialized = NS_TRUE1;
417
418 Ns_MutexInit(&plock);
419 Ns_MutexSetName(&plock, "ns:proxy");
420
421 Nsd_LibInit();
422
423 Tcl_DStringInit(&defexec);
424 Ns_BinPath(&defexec, NSPROXY_HELPER"nsproxy-helper", (char *)0L);
425
426 Tcl_InitHashTable(&pools, TCL_STRING_KEYS(0));
427 Ns_RegisterAtShutdown(Shutdown, NULL((void*)0));
428 Ns_RegisterProcInfo((ns_funcptr_t)Shutdown, "nsproxy:shutdown", NULL((void*)0));
429
430 Ns_LogNsProxyDebug = Ns_CreateLogSeverity("Debug(nsproxy)");
431 }
432}
433
434
435/*
436 *----------------------------------------------------------------------
437 *
438 * Ns_ProxyTclInit --
439 *
440 * Initialize the Tcl interface.
441 *
442 * Results:
443 * TCL_OK.
444 *
445 * Side effects:
446 * Adds the ns_proxy command to given interp.
447 *
448 *----------------------------------------------------------------------
449 */
450
451int
452Ns_ProxyTclInit(Tcl_Interp *interp)
453{
454 InterpData *idataPtr;
455
456 idataPtr = ns_calloc(1u, sizeof(InterpData));
457 Tcl_InitHashTable(&idataPtr->ids, TCL_STRING_KEYS(0));
458 Tcl_InitHashTable(&idataPtr->cnts, TCL_ONE_WORD_KEYS(1));
459 Tcl_SetAssocData(interp, ASSOC_DATA"nsproxy:data", DeleteData, idataPtr);
460 (void)Tcl_CreateObjCommand(interp, "ns_proxy", ProxyObjCmd, idataPtr, NULL((void*)0));
461
462 return TCL_OK0;
463}
464
465
466/*
467 *----------------------------------------------------------------------
468 *
469 * Ns_ProxyMain --
470 *
471 * Main loop for nsproxy worker processes. Initialize Tcl interp and loop
472 * processing requests. On communication errors or when the peer closes
473 * its write-pipe, worker process exits gracefully.
474 *
475 * Results:
476 * Always zero.
477 *
478 * Side effects:
479 * None.
480 *
481 *----------------------------------------------------------------------
482 */
483
484int
485Ns_ProxyMain(int argc, char *const*argv, Tcl_AppInitProc *init)
486{
487 Tcl_Interp *interp;
488 Worker proc;
489 int result, max;
490 Tcl_DString in, out;
491 const char *script, *dots, *uarg = NULL((void*)0), *user;
492 char *group = NULL((void*)0), *active;
493 uint16 major, minor;
494 size_t activeSize;
495
496 /*
497 * The call to Tcl_FindExecutable() must be done before we ever
498 * attempt any Tcl related call.
499 */
500 Tcl_FindExecutable(argv[0]);
501
502 Nsproxy_LibInit();
503
504 if (argc > 4 || argc < 3) {
505 char *pgm = strrchr(argv[0], INTCHAR('/')((int)((unsigned char)(('/')))));
506 Ns_Fatal("usage: %s pool id ?command?", (pgm != NULL((void*)0)) ? (pgm+1) : argv[0]);
507 }
508 if (argc < 4) {
509 active = NULL((void*)0);
510 activeSize = 0;
511 max = -1;
512 } else {
513 active = argv[3];
514 activeSize = strlen(active);
515 max = (int)activeSize - 8;
516 if (max < 0) {
517 active = NULL((void*)0);
518 }
519 }
520
521 /*
522 * Initialize Worker structure
523 */
524 memset(&proc, 0, sizeof(proc));
525
526 /*
527 * Move the proxy input and output fd's from 0 and 1 to avoid
528 * protocol errors with scripts accessing stdin and stdout.
529 * Stdin is open on /dev/null and stdout is dup'ed to stderr.
530 */
531
532 major = htons(MAJOR_VERSION)__bswap_16 (1);
533 minor = htons(MINOR_VERSION)__bswap_16 (1);
534 proc.pid = NS_INVALID_PID(-1);
535
536 proc.rfd = ns_dup(0)fcntl((0), 1030, 0);
537 if (proc.rfd < 0) {
538 Ns_Fatal("nsproxy: dup: %s", strerror(errno(*__errno_location ())));
539 }
540 proc.wfd = ns_dup(1)fcntl((1), 1030, 0);
541 if (proc.wfd < 0) {
542 Ns_Fatal("nsproxy: dup: %s", strerror(errno(*__errno_location ())));
543 }
544 ns_closeclose(0);
545 if (ns_openopen("/dev/null", O_RDONLY00 | O_CLOEXEC02000000, 0) != 0) {
546 Ns_Fatal("nsproxy: open: %s", strerror(errno(*__errno_location ())));
547 }
548 ns_closeclose(1);
549 if (ns_dup(2)fcntl((2), 1030, 0) != 1) {
550 Ns_Fatal("nsproxy: dup: %s", strerror(errno(*__errno_location ())));
551 }
552
553 /*
554 * Make sure possible child processes do not inherit this one.
555 * As, when the user evaluates the "exec" command, the child
556 * process(es) will otherwise inherit the descriptor and keep
557 * it open even if the proxy process is killed in the meantime.
558 * This will of course block the caller, possibly forever.
559 */
560
561 (void)Ns_CloseOnExec(proc.wfd);
562
563 /*
564 * Create the interp, initialize with user init proc, if any.
565 */
566
567 interp = Ns_TclCreateInterp();
568 if (init != NULL((void*)0)) {
569 if ((*init)(interp) != TCL_OK0) {
570 Ns_Fatal("nsproxy: init: %s", Tcl_GetStringResult(interp));
571 }
572 }
573
574 /*
575 * Parse encoded user/group information. Those are
576 * optionally encoded in the passed pool name:
577 *
578 * pool?:username_or_uid?:groupname_or_gid??
579 *
580 * Examples:
581 *
582 * mypool
583 * mypool:myname
584 * mypool:myname:mygroup
585 *
586 * The uid/gid fiddling code is replicated from the Ns_Main().
587 *
588 * etc...
589 */
590
591 user = strchr(argv[1], INTCHAR(':')((int)((unsigned char)((':')))));
592 if (user != NULL((void*)0)) {
593 uarg = ns_strdup(user + 1);
594 user = uarg;
595 group = strchr(user, INTCHAR(':')((int)((unsigned char)((':')))));
596 if (group != NULL((void*)0)) {
597 *group = 0;
598 group++;
599 }
600 }
601
602 if (Ns_SetGroup(group) == NS_ERROR || Ns_SetUser(user) == NS_ERROR) {
603 Ns_Fatal("nsproxy: unable to switch to user '%s', group '%s'", user, group);
604 }
605
606 /*
607 * Loop continuously processing proxy requests.
608 */
609
610 Tcl_DStringInit(&in);
611 Tcl_DStringInit(&out);
612
613 while (RecvBuf(&proc, NULL((void*)0), &in) == NS_TRUE1) {
614 Req req, *reqPtr = &req;
615 uint32_t len;
616
617 if (Tcl_DStringLength(&in)((&in)->length) < (int)sizeof(Req)) {
618 break;
619 }
620
621 memcpy(&req, in.string, sizeof(req));
622
623 if (reqPtr->major != major || reqPtr->minor != minor) {
624 Ns_Fatal("nsproxy: version mismatch");
625 }
626 len = ntohl(reqPtr->len)__bswap_32 (reqPtr->len);
627 if (len == 0) {
628 Export(NULL((void*)0), TCL_OK0, &out);
629 } else if (len > 0) {
630 script = Tcl_DStringValue(&in)((&in)->string) + sizeof(Req);
631 if (active != NULL((void*)0)) {
632 int n = (int)len;
633
634 if (n < max) {
635 dots = NS_EMPTY_STRING;
636 } else {
637 dots = " ...";
638 n = max;
639 }
640 snprintf(active, activeSize, "{%.*s%s}", n, script, dots)__builtin___snprintf_chk (active, activeSize, 2 - 1, __builtin_object_size
(active, 2 > 1), "{%.*s%s}", n, script, dots)
;
641 }
642 result = Tcl_EvalEx(interp, script, (int)len, 0);
643 Export(interp, result, &out);
644 if (active != NULL((void*)0)) {
645 assert(max > 0)((void) (0));
646 memset(active, ' ', (size_t)max);
647 }
648 } else {
649 Ns_Fatal("nsproxy: invalid length");
650 }
651 if (SendBuf(&proc, NULL((void*)0), &out) == NS_FALSE0) {
652 break;
653 }
654 Tcl_DStringSetLength(&in, 0);
655 Tcl_DStringSetLength(&out, 0);
656 }
657
658 if (uarg != NULL((void*)0)) {
659 ns_free((char *)uarg);
660 }
661 Tcl_DStringFree(&in);
662 Tcl_DStringFree(&out);
663
664 return 0;
665}
666
667
668/*
669 *----------------------------------------------------------------------
670 *
671 * Ns_ProxyCleanup --
672 *
673 * Tcl trace to release any proxy handles
674 * held in the current interp
675 *
676 * Results:
677 * Standard Tcl result.
678 *
679 * Side effects:
680 * None.
681 *
682 *----------------------------------------------------------------------
683 */
684
685int
686Ns_ProxyCleanup(Tcl_Interp *interp, const void *UNUSED(arg)UNUSED_arg __attribute__((__unused__)))
687{
688 InterpData *idataPtr = Tcl_GetAssocData(interp, ASSOC_DATA"nsproxy:data", NULL((void*)0));
689
690 if (idataPtr != NULL((void*)0)) {
691 ReleaseHandles(interp, idataPtr);
692 }
693
694 return TCL_OK0;
695}
696
697
698/*
699 *----------------------------------------------------------------------
700 *
701 * Shutdown --
702 *
703 * Server trace to timely shutdown proxy system
704 * including stopping the reaper thread.
705 *
706 * Results:
707 * None.
708 *
709 * Side effects:
710 * None.
711 *
712 *----------------------------------------------------------------------
713 */
714
715void
716Shutdown(const Ns_Time *timeoutPtr, void *UNUSED(arg)UNUSED_arg __attribute__((__unused__)))
717{
718 Pool *poolPtr;
719 Proxy *proxyPtr, *tmpPtr;
720 Tcl_HashSearch search;
721 int reap;
722 Ns_ReturnCode status;
723
724 /*
725 * Cleanup all known pools. This will put all idle
726 * proxies on the close list. At this point, there
727 * should be no running nor detached proxies.
728 * If yes, we will leak memory on exit (proxies and
729 * the whole pool will be left un-freed).
730 */
731
732 if (timeoutPtr == NULL((void*)0)) {
733 Tcl_HashEntry *hPtr;
734
735 Ns_MutexLock(&plock);
736 hPtr = Tcl_FirstHashEntry(&pools, &search);
737 while (hPtr != NULL((void*)0)) {
738 poolPtr = (Pool *)Tcl_GetHashValue(hPtr)((hPtr)->clientData);
739 Ns_MutexLock(&poolPtr->lock);
740 poolPtr->maxworker = 0; /* Disable creation of new workers */
741 proxyPtr = poolPtr->firstPtr;
742 while (proxyPtr != NULL((void*)0)) {
743 if (proxyPtr->workerPtr != NULL((void*)0)) {
744 CloseWorker(proxyPtr->workerPtr, &proxyPtr->conf.twait);
745 }
746 tmpPtr = proxyPtr->nextPtr;
747 FreeProxy(proxyPtr);
748 proxyPtr = tmpPtr;
749 }
750 Ns_MutexUnlock(&poolPtr->lock);
751 Tcl_DeleteHashEntry(hPtr);
752 if (poolPtr->nused == 0) {
753 FreePool(poolPtr);
754 } else {
755 Ns_Log(Warning, "nsproxy: [%s]: has %d used proxies",
756 poolPtr->name, poolPtr->nused);
757 }
758 hPtr = Tcl_NextHashEntry(&search);
759 }
760 Tcl_DeleteHashTable(&pools);
761 Ns_MutexUnlock(&plock);
762 return;
763 }
764
765 Ns_MutexLock(&plock);
766 reap = firstClosePtr != NULL((void*)0) || reaperState != Stopped;
767 Ns_MutexUnlock(&plock);
768
769 if (reap == 0) {
770 return;
771 }
772
773 /*
774 * There is something on the close list. Start
775 * the reaper thread if not done already and
776 * wait for it to gracefully exit.
777 */
778
779 Ns_Log(Notice, "nsproxy: shutdown started");
780 ReapProxies();
781 Ns_MutexLock(&plock);
782 reaperState = Stopping;
783 status = NS_OK;
784 Ns_CondSignal(&pcond);
785 while (reaperState != Stopped && status == NS_OK) {
786 status = Ns_CondTimedWait(&pcond, &plock, timeoutPtr);
787 if (status != NS_OK) {
788 Ns_Log(Warning, "nsproxy: timeout waiting for reaper exit");
789 }
790 }
791 Ns_MutexUnlock(&plock);
792
793 Ns_Log(Notice, "nsproxy: shutdown complete");
794}
795
796
797/*
798 *----------------------------------------------------------------------
799 *
800 * Ns_ProxyGet --
801 *
802 * Get one proxy handle for the given pool.
803 *
804 * Results:
805 * None.
806 *
807 * Side effects:
808 * None.
809 *
810 *----------------------------------------------------------------------
811 */
812int
813Ns_ProxyGet(Tcl_Interp *interp, const char *poolName, PROXY* handlePtr, Ns_Time *timePtr)
814{
815 Pool *poolPtr;
816 Proxy *proxyPtr;
817 Err err;
818 int result;
819
820 /*
821 * Get just one proxy from the pool
822 */
823 poolPtr = GetPool(poolName, NULL((void*)0));
824
825 err = PopProxy(poolPtr, &proxyPtr, 1, timePtr);
826 if (unlikely(err != 0)(__builtin_expect((err != 0), 0))) {
827 Ns_TclPrintfResult(interp, "could not allocate from pool \"%s\": %s",
828 poolPtr->name, errMsg[err]);
829 ProxyError(interp, err);
830 result = TCL_ERROR1;
831
832 } else if (CheckProxy(interp, proxyPtr) != ENone) {
833 /*
834 * No proxy connection.
835 */
836 PushProxy(proxyPtr);
837 Ns_CondBroadcast(&poolPtr->cond);
838 result = TCL_ERROR1;
839
840 } else {
841 /*
842 * Valid proxy for connection.
843 */
844 *handlePtr = (PROXY *)proxyPtr;
845 result = TCL_OK0;
846
847 }
848
849 return result;
850}
851
852
853/*
854 *----------------------------------------------------------------------
855 *
856 * Ns_ProxyPut --
857 *
858 * Return the proxy handle back.
859 *
860 * Results:
861 * None.
862 *
863 * Side effects:
864 * None.
865 *
866 *----------------------------------------------------------------------
867 */
868
869void
870Ns_ProxyPut(PROXY handle)
871{
872 PushProxy((Proxy *)handle);
873}
874
875
876/*
877 *----------------------------------------------------------------------
878 *
879 * Ns_ProxyEval --
880 *
881 * Evaluates the script in the proxy.
882 *
883 * Results:
884 * Standard Tcl result.
885 *
886 * Side effects:
887 * None.
888 *
889 *----------------------------------------------------------------------
890 */
891
892int Ns_ProxyEval(Tcl_Interp *interp, PROXY handle, const char *script, const Ns_Time *timeoutPtr)
893{
894 return Eval(interp, (Proxy *)handle, script, timeoutPtr);
895}
896
897/*
898 *----------------------------------------------------------------------
899 *
900 * ExecWorker --
901 *
902 * Create a new proxy worker process.
903 *
904 * Results:
905 * Pointer to new Worker or NULL on error.
906 *
907 * Side effects:
908 * None.
909 *
910 *----------------------------------------------------------------------
911 */
912
913static Worker *
914ExecWorker(Tcl_Interp *interp, const Proxy *proxyPtr)
915{
916 Pool *poolPtr;
917 char *argv[5];
918 char active[100];
919 Worker *workerPtr;
920 int rpipe[2], wpipe[2];
921 size_t len;
922 pid_t pid;
923
924 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
925 NS_NONNULL_ASSERT(proxyPtr != NULL)((void) (0));
926
927 poolPtr = proxyPtr->poolPtr;
928 len = sizeof(active) - 1;
929 memset(active, ' ', len);
930 active[len] = '\0';
931
932 Ns_MutexLock(&poolPtr->lock);
933 argv[0] = ns_strdup(poolPtr->exec);
934 argv[1] = ns_strdup(poolPtr->name);
935 Ns_MutexUnlock(&poolPtr->lock);
936
937 argv[2] = proxyPtr->id;
938 argv[3] = active;
939 argv[4] = NULL((void*)0);
940
941 if (ns_pipe(rpipe) != 0) {
942 Ns_TclPrintfResult(interp, "pipe failed: %s", Tcl_PosixError(interp));
943 return NULL((void*)0);
944 }
945 if (ns_pipe(wpipe) != 0) {
946 Ns_TclPrintfResult(interp, "pipe failed: %s", Tcl_PosixError(interp));
947 ns_closeclose(rpipe[0]);
948 ns_closeclose(rpipe[1]);
949 return NULL((void*)0);
950 }
951
952 pid = Ns_ExecArgv(poolPtr->exec, NULL((void*)0), rpipe[0], wpipe[1], argv, poolPtr->env);
953
954 ns_closeclose(rpipe[0]);
955 ns_closeclose(wpipe[1]);
956
957 ns_free(argv[0]);
958 ns_free(argv[1]);
959
960 if (pid == NS_INVALID_PID(-1)) {
961 Ns_TclPrintfResult(interp, "exec failed: %s", Tcl_PosixError(interp));
962 ns_closeclose(wpipe[0]);
963 ns_closeclose(rpipe[1]);
964 return NULL((void*)0);
965 }
966
967 workerPtr = ns_calloc(1u, sizeof(Worker));
968 workerPtr->poolPtr = proxyPtr->poolPtr;
969 workerPtr->pid = pid;
970 workerPtr->rfd = wpipe[0];
971 workerPtr->wfd = rpipe[1];
972
973 SetExpire(workerPtr, &proxyPtr->conf.tidle);
974
975 Ns_Log(Ns_LogNsProxyDebug, "nsproxy: worker process %ld started", (long) workerPtr->pid);
976
977 return workerPtr;
978}
979
980
981/*
982 *----------------------------------------------------------------------
983 *
984 * SetExpire --
985 *
986 * Sets the absolute expire time for the worker process.
987 *
988 * Results:
989 * None.
990 *
991 * Side effects:
992 * None.
993 *
994 *----------------------------------------------------------------------
995 */
996
997static void
998SetExpire(Worker *workerPtr, const Ns_Time *timePtr)
999{
1000 NS_NONNULL_ASSERT(workerPtr != NULL)((void) (0));
1001
1002 Ns_Log(Ns_LogNsProxyDebug, "set expire in %ld ms for pool %s worker %ld",
1003 timePtr == NULL((void*)0) ? -1 : (long)Ns_TimeToMilliseconds(timePtr),
1004 workerPtr->poolPtr->name, (long)workerPtr->pid);
1005
1006 if (timePtr != NULL((void*)0)) {
1007 Ns_GetTime(&workerPtr->expire);
1008 Ns_IncrTime(&workerPtr->expire, timePtr->sec, timePtr->usec);
1009 } else {
1010 workerPtr->expire.sec = TIME_T_MAX9223372036854775807L;
1011 workerPtr->expire.usec = 0;
1012 }
1013}
1014
1015
1016/*
1017 *----------------------------------------------------------------------
1018 *
1019 * Eval --
1020 *
1021 * Send a script and wait for and receive a response.
1022 *
1023 * Results:
1024 * Tcl result code from evaluating script, or TCL_ERROR if
1025 * any communication errors or timeouts.
1026 *
1027 * Side effects:
1028 * Will leave proxy response or error message in interp.
1029 *
1030 *----------------------------------------------------------------------
1031 */
1032
1033static int
1034Eval(Tcl_Interp *interp, Proxy *proxyPtr, const char *script, const Ns_Time *timeoutPtr)
1035{
1036 Err err;
1037 int status = TCL_ERROR1;
1038 Ns_Time startTime;
1039
1040 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
1041 NS_NONNULL_ASSERT(proxyPtr != NULL)((void) (0));
1042
1043 Ns_GetTime(&startTime);
1044
1045 err = Send(interp, proxyPtr, script);
1046 if (err == ENone) {
1047 err = Wait(interp, proxyPtr, timeoutPtr);
1048 if (err == ENone) {
1049 (void) Recv(interp, proxyPtr, &status);
1050 }
1051 /*
1052 * Don't count check-proxy calls (script == NULL)
1053 */
1054 if (script != NULL((void*)0)) {
1055 Ns_Time endTime, diffTime;
1056
1057 Ns_GetTime(&endTime);
1058 (void)Ns_DiffTime(&endTime, &startTime, &diffTime);
1059 if (Ns_DiffTime(&proxyPtr->conf.logminduration, &diffTime, NULL((void*)0)) < 1) {
1060 Ns_Log(Notice, "nsproxy %s duration " NS_TIME_FMT"%" "l" "d" ".%06ld" " secs: '%s'",
1061 proxyPtr->poolPtr->name, (int64_t)diffTime.sec, diffTime.usec, script);
1062 }
1063
1064 Ns_Log(Debug, "Eval calls GetStats <%s>", script);
1065 GetStats(proxyPtr);
1066 }
1067 }
1068
1069 return status;
1070}
1071
1072
1073/*
1074 *----------------------------------------------------------------------
1075 *
1076 * GetStats --
1077 *
1078 * Obtain run time statistics
1079 *
1080 * Results:
1081 * None.
1082 *
1083 * Side effects:
1084 * Update the pool's run time
1085 *
1086 *----------------------------------------------------------------------
1087 */
1088static void
1089GetStats(const Proxy *proxyPtr)
1090{
1091 Ns_Time now, runTimeSpan;
1092
1093 NS_NONNULL_ASSERT(proxyPtr != NULL)((void) (0));
1094
1095 Ns_GetTime(&now);
1096 Ns_DiffTime(&now, &proxyPtr->when, &runTimeSpan);
1097 Ns_IncrTime(&proxyPtr->poolPtr->runTime, runTimeSpan.sec, runTimeSpan.usec);
1098 proxyPtr->poolPtr->nruns++;
1099}
1100
1101/*
1102 *----------------------------------------------------------------------
1103 *
1104 * Send --
1105 *
1106 * Send a script to a proxy.
1107 *
1108 * Results:
1109 * Proxy Err code.
1110 *
1111 * Side effects:
1112 * Will format error message in given interp on failure.
1113 *
1114 *----------------------------------------------------------------------
1115 */
1116
1117static Err
1118Send(Tcl_Interp *interp, Proxy *proxyPtr, const char *script)
1119{
1120 Err err = ENone;
1121 Req req;
1122
1123 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
1124 NS_NONNULL_ASSERT(proxyPtr != NULL)((void) (0));
1125
1126 if (proxyPtr->workerPtr == NULL((void*)0)) {
1127 err = EDead;
1128 } else if (proxyPtr->state != Idle) {
1129 err = EBusy;
1130 } else {
1131 proxyPtr->numruns++;
1132 if (proxyPtr->conf.maxruns > 0
1133 && proxyPtr->numruns > proxyPtr->conf.maxruns) {
1134 Ns_Log(Notice, "proxy maxrun reached pool %s worker %ld",
1135 proxyPtr->poolPtr->name, (long)proxyPtr->workerPtr->pid);
1136 CloseProxy(proxyPtr);
1137 err = CreateWorker(interp, proxyPtr);
1138 }
1139 if (err == ENone) {
1140 size_t len = script == NULL((void*)0) ? 0u : strlen(script);
1141
1142 req.len = htonl((uint32_t)len)__bswap_32 ((uint32_t)len);
1143 req.major = htons(MAJOR_VERSION)__bswap_16 (1);
1144 req.minor = htons(MINOR_VERSION)__bswap_16 (1);
1145 Tcl_DStringSetLength(&proxyPtr->in, 0);
1146 Tcl_DStringAppend(&proxyPtr->in, (char *) &req, sizeof(req));
1147 if (len > 0u) {
1148 Tcl_DStringAppend(&proxyPtr->in, script, (int)len);
1149 }
1150 proxyPtr->state = Busy;
1151
1152 /*
1153 * Proxy is active, put it on the
1154 * head of the run queue,
1155 */
1156
1157 Ns_GetTime(&proxyPtr->when);
1158
1159 Ns_MutexLock(&proxyPtr->poolPtr->lock);
1160 proxyPtr->runPtr = proxyPtr->poolPtr->runPtr;
1161 proxyPtr->poolPtr->runPtr = proxyPtr;
1162 Ns_MutexUnlock(&proxyPtr->poolPtr->lock);
1163
1164 if (script != NULL((void*)0)) {
1165 Ns_Log(Ns_LogNsProxyDebug, "proxy send pool %s worker %ld: %s",
1166 proxyPtr->poolPtr->name, (long)proxyPtr->workerPtr->pid, script);
1167 }
1168
1169 if (SendBuf(proxyPtr->workerPtr, &proxyPtr->conf.tsend,
1170 &proxyPtr->in) == NS_FALSE0) {
1171 err = ESend;
1172 }
1173 }
1174 }
1175
1176 if (err != ENone) {
1177 Ns_TclPrintfResult(interp, "could not send script \"%s\" to proxy \"%s\": %s",
1178 script == NULL((void*)0) ? NS_EMPTY_STRING : script,
1179 proxyPtr->id, errMsg[err]);
1180 ProxyError(interp, err);
1181 }
1182
1183 return err;
1184}
1185
1186
1187/*
1188 *----------------------------------------------------------------------
1189 *
1190 * Wait --
1191 *
1192 * Wait for response from proxy process.
1193 *
1194 * Results:
1195 * Proxy Err code.
1196 *
1197 * Side effects:
1198 * Will format error message in given interp on failure.
1199 *
1200 *----------------------------------------------------------------------
1201 */
1202
1203static Err
1204Wait(Tcl_Interp *interp, Proxy *proxyPtr, const Ns_Time *timeoutPtr)
1205{
1206 Err err = ENone;
1207
1208 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
1209 NS_NONNULL_ASSERT(proxyPtr != NULL)((void) (0));
1210
1211 if (proxyPtr->state == Idle) {
1212 err = EIdle;
1213 } else if (proxyPtr->workerPtr == NULL((void*)0)) {
1214 err = EDead;
1215 } else if (proxyPtr->state != Done) {
1216 time_t ms;
1217
1218 if (timeoutPtr != NULL((void*)0)) {
1219 ms = Ns_TimeToMilliseconds(timeoutPtr);
1220 } else {
1221 ms = -1;
1222 }
1223 if (ms <= 0) {
1224 ms = Ns_TimeToMilliseconds(&proxyPtr->conf.teval);
1225 }
1226 if (ms <= 0) {
1227 ms = -1;
1228 }
1229 if (!WaitFd(proxyPtr->workerPtr->rfd, POLLIN0x001, (long)ms)) {
1230 err = EEvalTimeout;
1231 } else {
1232 proxyPtr->state = Done;
1233 }
1234 }
1235
1236 if (err != ENone) {
1237 Ns_TclPrintfResult(interp, "could not wait for proxy \"%s\": %s",
1238 proxyPtr->id, errMsg[err]);
1239 ProxyError(interp, err);
1240 }
1241
1242 return err;
1243}
1244
1245
1246/*
1247 *----------------------------------------------------------------------
1248 *
1249 * Recv --
1250 *
1251 * Receive proxy results.
1252 *
1253 * Results:
1254 * Proxy Err code.
1255 *
1256 * Side effects:
1257 * Will append proxy results or error message to given interp.
1258 *
1259 *----------------------------------------------------------------------
1260 */
1261
1262static Err
1263Recv(Tcl_Interp *interp, Proxy *proxyPtr, int *resultPtr)
1264{
1265 Err err = ENone;
1266
1267 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
1268 NS_NONNULL_ASSERT(proxyPtr != NULL)((void) (0));
1269 NS_NONNULL_ASSERT(resultPtr != NULL)((void) (0));
1270
1271 if (proxyPtr->state == Idle) {
1272 err = EIdle;
1273 } else if (proxyPtr->state == Busy) {
1274 err = ENoWait;
1275 } else {
1276 Tcl_DStringSetLength(&proxyPtr->out, 0);
1277 if (RecvBuf(proxyPtr->workerPtr, &proxyPtr->conf.trecv,
1278 &proxyPtr->out) == NS_FALSE0) {
1279 err = ERecv;
1280 } else if (Import(interp, &proxyPtr->out, resultPtr) != TCL_OK0) {
1281 err = EImport;
1282 } else {
1283 proxyPtr->state = Idle;
1284 }
1285 ResetProxy(proxyPtr);
1286 }
1287
1288 if (err != ENone) {
1289 Ns_TclPrintfResult(interp, "could not receive from proxy \"%s\": %s",
1290 proxyPtr->id, errMsg[err]);
1291 ProxyError(interp, err);
1292 }
1293
1294 return err;
1295}
1296
1297/*
1298 *----------------------------------------------------------------------
1299 *
1300 * SendBuf --
1301 *
1302 * Send a dstring buffer to the specified worker process.
1303 *
1304 * Results:
1305 * NS_TRUE if sent, NS_FALSE on error.
1306 *
1307 * Side effects:
1308 * None.
1309 *
1310 *----------------------------------------------------------------------
1311 */
1312
1313static bool_Bool
1314SendBuf(const Worker *workerPtr, const Ns_Time *timePtr, const Tcl_DString *dsPtr)
1315{
1316 ssize_t n;
1317 uint32 ulen;
1318 struct iovec iov[2];
1319 Ns_Time end;
1320 bool_Bool success = NS_TRUE1;
1321
1322 NS_NONNULL_ASSERT(workerPtr != NULL)((void) (0));
1323 NS_NONNULL_ASSERT(dsPtr != NULL)((void) (0));
1324
1325 if (timePtr != NULL((void*)0)) {
1326 Ns_GetTime(&end);
1327 Ns_IncrTime(&end, timePtr->sec, timePtr->usec);
1328 }
1329
1330 ulen = htonl((unsigned int)dsPtr->length)__bswap_32 ((unsigned int)dsPtr->length);
1331 iov[0].iov_base = (void *)&ulen;
1332 iov[0].iov_len = sizeof(ulen);
1333 iov[1].iov_base = dsPtr->string;
1334 iov[1].iov_len = (size_t)dsPtr->length;
1335
1336 while ((iov[0].iov_len + iov[1].iov_len) > 0u) {
1337 do {
1338 n = writev(workerPtr->wfd, iov, 2);
1339 } while (n == -1 && errno(*__errno_location ()) == NS_EINTR4);
1340
1341 if (n == -1) {
1342 long waitMs;
1343
1344 if ((errno(*__errno_location ()) != NS_EAGAIN11) && (errno(*__errno_location ()) != NS_EWOULDBLOCK11)) {
1345 success = NS_FALSE0;
1346 break;
1347
1348 } else if (timePtr != NULL((void*)0)) {
1349 waitMs = GetTimeDiff(&end);
1350 if (waitMs < 0) {
1351 success = NS_FALSE0;
1352 break;
1353 }
1354 } else {
1355 waitMs = -1;
1356 }
1357 if (!WaitFd(workerPtr->wfd, POLLOUT0x004, waitMs)) {
1358 success = NS_FALSE0;
1359 break;
1360 }
1361 } else if (n > 0) {
1362 UpdateIov(iov, (size_t)n);
1363 }
1364 }
1365
1366 return success;
1367}
1368
1369
1370/*
1371 *----------------------------------------------------------------------
1372 *
1373 * RecvBuf --
1374 *
1375 * Receive a dstring buffer.
1376 *
1377 * Results:
1378 * NS_TRUE if sent, NS_FALSE on error.
1379 *
1380 * Side effects:
1381 * Will resize output dstring as needed.
1382 *
1383 *----------------------------------------------------------------------
1384 */
1385
1386static bool_Bool
1387RecvBuf(const Worker *workerPtr, const Ns_Time *timePtr, Tcl_DString *dsPtr)
1388{
1389 uint32 ulen = 0u;
1390 ssize_t n;
1391 size_t avail;
1392 struct iovec iov[2];
1393 Ns_Time end;
1394 bool_Bool success = NS_TRUE1;
1395
1396 NS_NONNULL_ASSERT(workerPtr != NULL)((void) (0));
1397 NS_NONNULL_ASSERT(dsPtr != NULL)((void) (0));
1398
1399 if (timePtr != NULL((void*)0)) {
1400 Ns_GetTime(&end);
1401 Ns_IncrTime(&end, timePtr->sec, timePtr->usec);
1402 }
1403
1404 avail = (size_t)dsPtr->spaceAvl - 1u;
1405 iov[0].iov_base = (void *)&ulen;
1406 iov[0].iov_len = sizeof(ulen);
1407 iov[1].iov_base = dsPtr->string;
1408 iov[1].iov_len = avail;
1409
1410 while (iov[0].iov_len > 0) {
1411 do {
1412 n = readv(workerPtr->rfd, iov, 2);
1413 } while ((n == -1) && (errno(*__errno_location ()) == NS_EINTR4));
1414
1415 if (n == 0) {
1416 success = NS_FALSE0; /* EOF */
1417 break;
1418
1419 } else if (n < 0) {
1420 long waitMs;
1421
1422 if (errno(*__errno_location ()) != NS_EAGAIN11 && errno(*__errno_location ()) != NS_EWOULDBLOCK11) {
1423 success = NS_FALSE0;
1424 break;
1425
1426 } else if (timePtr != NULL((void*)0)) {
1427 waitMs = GetTimeDiff(&end);
1428 if (waitMs < 0) {
1429 success = NS_FALSE0;
1430 break;
1431 }
1432 } else {
1433 waitMs = -1;
1434 }
1435 if (!WaitFd(workerPtr->rfd, POLLIN0x001, waitMs)) {
1436 success = NS_FALSE0;
1437 break;
1438 }
1439 } else if (n > 0) {
1440 UpdateIov(iov, (size_t)n);
1441 }
1442 }
1443 if (success) {
1444 char *ptr;
1445 ssize_t len;
1446
1447 n = (ssize_t)(avail - iov[1].iov_len);
1448 Tcl_DStringSetLength(dsPtr, (int)n);
1449 len = (ssize_t)ntohl(ulen)__bswap_32 (ulen);
1450 Tcl_DStringSetLength(dsPtr, (int)len);
1451 len -= n;
1452 ptr = dsPtr->string + n;
1453
1454 while (len > 0) {
1455 do {
1456 n = ns_readread(workerPtr->rfd, ptr, (size_t)len);
1457 } while ((n == -1) && (errno(*__errno_location ()) == NS_EINTR4));
1458
1459 if (n == 0) {
1460 success = NS_FALSE0; /* EOF */
1461 break;
1462
1463 } else if (n < 0) {
1464 long waitMs;
1465
1466 if (errno(*__errno_location ()) != NS_EAGAIN11 && errno(*__errno_location ()) != NS_EWOULDBLOCK11) {
1467 success = NS_FALSE0;
1468 break;
1469
1470 } else if (timePtr != NULL((void*)0)) {
1471 waitMs = GetTimeDiff(&end);
1472 if (waitMs < 0) {
1473 success = NS_FALSE0;
1474 break;
1475 }
1476 } else {
1477 waitMs = -1;
1478 }
1479 if (!WaitFd(workerPtr->rfd, POLLIN0x001, waitMs)) {
1480 success = NS_FALSE0;
1481 break;
1482 }
1483 } else if (n > 0) {
1484 len -= n;
1485 ptr += n;
1486 }
1487 }
1488 }
1489
1490 return success;
1491}
1492
1493
1494/*
1495 *----------------------------------------------------------------------
1496 *
1497 * WaitFd --
1498 *
1499 * Waits for the given event on the worker pipe.
1500 *
1501 * Results:
1502 * NS_TRUE if event received, NS_FALSE on error.
1503 *
1504 * Side effects:
1505 * None.
1506 *
1507 *----------------------------------------------------------------------
1508 */
1509
1510static bool_Bool
1511WaitFd(int fd, short events, long ms)
1512{
1513 struct pollfd pfd;
1514 int n;
1515
1516 pfd.fd = fd;
1517 pfd.events = events | POLLPRI0x002 | POLLERR0x008;
1518 pfd.revents = pfd.events;
1519 do {
1520 n = ns_poll(&pfd, 1, ms);
1521 } while (n == -1 && errno(*__errno_location ()) == NS_EINTR4);
1522 if (n == -1) {
1523 n = 0;
1524 Ns_Log(Error, "nsproxy: poll failed: %s", strerror(errno(*__errno_location ())));
1525 }
1526
1527 return n;
1528}
1529
1530
1531/*
1532 *----------------------------------------------------------------------
1533 *
1534 * UpdateIov --
1535 *
1536 * Update the base and len in given iovec based on bytes
1537 * already processed.
1538 *
1539 * Results:
1540 * None.
1541 *
1542 * Side effects:
1543 * None.
1544 *
1545 *----------------------------------------------------------------------
1546 */
1547
1548static void
1549UpdateIov(struct iovec *iov, size_t n)
1550{
1551 NS_NONNULL_ASSERT(iov != NULL)((void) (0));
1552
1553 if (n >= iov[0].iov_len) {
1554 n -= iov[0].iov_len;
1555 iov[0].iov_base = NULL((void*)0);
1556 iov[0].iov_len = 0;
1557 } else {
1558 iov[0].iov_len -= n;
1559 iov[0].iov_base = (char *)(iov[0].iov_base) + n;
1560 n = 0;
1561 }
1562 iov[1].iov_len -= n;
1563 iov[1].iov_base = (char *)(iov[1].iov_base) + n;
1564}
1565
1566
1567/*
1568 *----------------------------------------------------------------------
1569 *
1570 * Export --
1571 *
1572 * Export result of Tcl, include error, to given dstring.
1573 *
1574 * Results:
1575 * None.
1576 *
1577 * Side effects:
1578 * Given dstring will contain response header and data.
1579 *
1580 *----------------------------------------------------------------------
1581 */
1582
1583static void
1584Export(Tcl_Interp *interp, int code, Tcl_DString *dsPtr)
1585{
1586 Res hdr;
1587 const char *einfo = NULL((void*)0), *ecode = NULL((void*)0), *result = NULL((void*)0);
1588 unsigned int clen = 0u, ilen = 0u, rlen = 0u;
1589
1590 NS_NONNULL_ASSERT(dsPtr != NULL)((void) (0));
1591
1592 if (interp != NULL((void*)0)) {
1593 if (code == TCL_OK0) {
1594 einfo = NULL((void*)0);
1595 ecode = NULL((void*)0);
1596 } else {
1597 ecode = Tcl_GetVar(interp, "errorCode", TCL_GLOBAL_ONLY)Tcl_GetVar2(interp, "errorCode", ((void*)0), 1);
1598 einfo = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY)Tcl_GetVar2(interp, "errorInfo", ((void*)0), 1);
1599 }
1600 clen = (ecode != NULL((void*)0)) ? ((unsigned int)strlen(ecode) + 1) : 0u;
1601 ilen = (einfo != NULL((void*)0)) ? ((unsigned int)strlen(einfo) + 1) : 0u;
1602 result = Tcl_GetStringResult(interp);
1603 rlen = (unsigned int)strlen(result);
1604 }
1605 hdr.code = htonl((unsigned int)code)__bswap_32 ((unsigned int)code);
1606 hdr.clen = htonl(clen)__bswap_32 (clen);
1607 hdr.ilen = htonl(ilen)__bswap_32 (ilen);
1608 hdr.rlen = htonl(rlen)__bswap_32 (rlen);
1609 Tcl_DStringAppend(dsPtr, (char *) &hdr, sizeof(hdr));
1610 if (clen > 0) {
1611 Tcl_DStringAppend(dsPtr, ecode, (int)clen);
1612 }
1613 if (ilen > 0) {
1614 Tcl_DStringAppend(dsPtr, einfo, (int)ilen);
1615 }
1616 if (rlen > 0) {
1617 Tcl_DStringAppend(dsPtr, result, (int)rlen);
1618 }
1619}
1620
1621
1622/*
1623 *----------------------------------------------------------------------
1624 *
1625 * Import --
1626 *
1627 * Import result of Tcl to given interp.
1628 *
1629 * Results:
1630 * Tcl result code from remote worker process.
1631 *
1632 * Side effects:
1633 * Will set interp result and error data as needed.
1634 *
1635 *----------------------------------------------------------------------
1636 */
1637
1638static int
1639Import(Tcl_Interp *interp, const Tcl_DString *dsPtr, int *resultPtr)
1640{
1641 int result = TCL_OK0;
1642
1643 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
1644 NS_NONNULL_ASSERT(dsPtr != NULL)((void) (0));
1645 NS_NONNULL_ASSERT(resultPtr != NULL)((void) (0));
1646
1647 if (dsPtr->length < (int)sizeof(Res)) {
1648 result = TCL_ERROR1;
1649
1650 } else {
1651 Res res, *resPtr = &res;
1652 const char *str = dsPtr->string + sizeof(Res);
1653 size_t rlen, clen, ilen;
1654
1655 memcpy(&res, dsPtr->string, sizeof(Res));
1656
1657 clen = ntohl(resPtr->clen)__bswap_32 (resPtr->clen);
1658 ilen = ntohl(resPtr->ilen)__bswap_32 (resPtr->ilen);
1659 rlen = ntohl(resPtr->rlen)__bswap_32 (resPtr->rlen);
1660 if (clen > 0) {
1661 Tcl_Obj *err = Tcl_NewStringObj(str, -1);
1662
1663 Tcl_SetObjErrorCode(interp, err);
1664 str += clen;
1665 }
1666 if (ilen > 0) {
1667 Tcl_AddErrorInfo(interp, str);
1668 str += ilen;
1669 }
1670 if (rlen > 0) {
1671 Tcl_SetObjResult(interp, Tcl_NewStringObj(str, -1));
1672 }
1673 *resultPtr = (int)ntohl(resPtr->code)__bswap_32 (resPtr->code);
1674 }
1675 return result;
1676}
1677
1678
1679
1680/*
1681 *----------------------------------------------------------------------
1682 *
1683 * StatsObjCmd --
1684 *
1685 * Implements "ns_proxy stats".
1686 *
1687 * Results:
1688 * Tcl result.
1689 *
1690 * Side effects:
1691 * None.
1692 *
1693 *----------------------------------------------------------------------
1694 */
1695static int
1696StatsObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1697{
1698 int result = TCL_OK0;
1699 char *pool;
1700 Ns_ObjvSpec args[] = {
1701 {"pool", Ns_ObjvString, &pool, NULL((void*)0)},
1702 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
1703 };
1704
1705 if (Ns_ParseObjv(NULL((void*)0), args, interp, 2, objc, objv) != NS_OK) {
1706 result = TCL_ERROR1;
1707
1708 } else {
1709 Tcl_DString ds, *dsPtr = &ds;
1710 Pool *poolPtr = GetPool(pool, clientData);
1711 int processes = 0;
1712 Proxy *proxyPtr;
1713
1714 Tcl_DStringInit(dsPtr);
1715 Ns_MutexLock(&plock);
1716 Ns_MutexLock(&poolPtr->lock);
1717
1718 for (proxyPtr = poolPtr->firstPtr; proxyPtr != NULL((void*)0); proxyPtr = proxyPtr->nextPtr) {
1719 if (proxyPtr->workerPtr != NULL((void*)0)) {
1720 processes ++;
1721 }
1722 }
1723 Ns_DStringPrintf(dsPtr, "proxies %" PRIuPTR"l" "u", poolPtr->nextid);
1724 Ns_DStringPrintf(dsPtr, " waiting %d", poolPtr->waiting);
1725 Ns_DStringPrintf(dsPtr, " maxworkers %d", poolPtr->maxworker);
1726 Ns_DStringPrintf(dsPtr, " free %d", poolPtr->nfree);
1727 Ns_DStringPrintf(dsPtr, " used %d", poolPtr->nused);
1728 Ns_DStringPrintf(dsPtr, " requests %" PRIuPTR"l" "u", poolPtr->nruns);
1729 Ns_DStringPrintf(dsPtr, " processes %d", processes);
1730 Tcl_DStringAppend(dsPtr, " runtime ", 9);
1731 Ns_DStringAppendTime(dsPtr, &poolPtr->runTime);
1732
1733 Ns_MutexUnlock(&poolPtr->lock);
1734 Ns_MutexUnlock(&plock);
1735
1736 Tcl_DStringResult(interp, dsPtr);
1737 }
1738
1739 return result;
1740}
1741
1742
1743/*
1744 *----------------------------------------------------------------------
1745 *
1746 * StopObjCmd --
1747 *
1748 * Implements "ns_proxy stop".
1749 *
1750 * Results:
1751 * Tcl result.
1752 *
1753 * Side effects:
1754 * None.
1755 *
1756 *----------------------------------------------------------------------
1757 */
1758static int
1759StopObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1760{
Duplicate code detected
1761 int result = TCL_OK0;
1762 char *pool, *handle = NULL((void*)0);
1763 Ns_ObjvSpec args[] = {
1764 {"pool", Ns_ObjvString, &pool, NULL((void*)0)},
1765 {"?handle", Ns_ObjvString, &handle, NULL((void*)0)},
1766 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
1767 };
1768
1769 if (Ns_ParseObjv(NULL((void*)0), args, interp, 2, objc, objv) != NS_OK) {
1770 result = TCL_ERROR1;
1771
1772 } else {
1773 Tcl_HashEntry *hPtr;
1774 Tcl_HashSearch search;
1775 Pool *thePoolPtr = GetPool(pool, clientData);
1776 int reap = 0;
1777
1778 Ns_MutexLock(&plock);
1779 for (hPtr = Tcl_FirstHashEntry(&pools, &search); hPtr != NULL((void*)0); hPtr = Tcl_NextHashEntry(&search)) {
1780 Pool *poolPtr = (Pool *)Tcl_GetHashValue(hPtr)((hPtr)->clientData);
1781
1782 if (thePoolPtr == poolPtr) {
1783 Proxy *proxyPtr;
1784
1785 Ns_MutexLock(&poolPtr->lock);
1786 for (proxyPtr = poolPtr->runPtr; proxyPtr != NULL((void*)0); proxyPtr = proxyPtr->runPtr) {
1787 reap += CloseWorkerOfProxy(proxyPtr, handle, &proxyPtr->conf.twait);
1788 }
1789 Ns_MutexUnlock(&poolPtr->lock);
1790 break;
1791 }
1792 }
1793 Ns_MutexUnlock(&plock);
1794 if (reap != 0) {
1795 ReapProxies();
1796 }
1797 }
1798 return result;
1799}
1800
1801/*
1802 *----------------------------------------------------------------------
1803 *
1804 * StopObjCmd --
1805 *
1806 * Implements "ns_proxy clear".
1807 *
1808 * Results:
1809 * Tcl result.
1810 *
1811 * Side effects:
1812 * None.
1813 *
1814 *----------------------------------------------------------------------
1815 */
1816
1817static int
1818ClearObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1819{
Similar code here
1820 int result = TCL_OK0;
1821 char *pool, *handle = NULL((void*)0);
1822 Ns_ObjvSpec args[] = {
1823 {"pool", Ns_ObjvString, &pool, NULL((void*)0)},
1824 {"?handle", Ns_ObjvString, &handle, NULL((void*)0)},
1825 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
1826 };
1827
1828 if (Ns_ParseObjv(NULL((void*)0), args, interp, 2, objc, objv) != NS_OK) {
1829 result = TCL_ERROR1;
1830
1831 } else {
1832 Tcl_HashEntry *hPtr;
1833 Tcl_HashSearch search;
1834 Pool *thePoolPtr = GetPool(pool, clientData);
1835 int reap = 0;
1836
1837 Ns_MutexLock(&plock);
1838 for (hPtr = Tcl_FirstHashEntry(&pools, &search); hPtr != NULL((void*)0); hPtr = Tcl_NextHashEntry(&search)) {
1839 Pool *poolPtr = (Pool *)Tcl_GetHashValue(hPtr)((hPtr)->clientData);
1840
1841 if (thePoolPtr == poolPtr) {
1842 Proxy *proxyPtr;
1843
1844 Ns_MutexLock(&poolPtr->lock);
1845 for (proxyPtr = poolPtr->firstPtr; proxyPtr != NULL((void*)0); proxyPtr = proxyPtr->nextPtr) {
1846 reap += CloseWorkerOfProxy(proxyPtr, handle, &proxyPtr->conf.twait);
1847 }
1848 Ns_MutexUnlock(&poolPtr->lock);
1849 break;
1850 }
1851 }
1852 Ns_MutexUnlock(&plock);
1853 if (reap != 0) {
1854 ReapProxies();
1855 }
1856 }
1857 return result;
1858}
1859
1860
1861
1862/*
1863 *----------------------------------------------------------------------
1864 *
1865 * ProxyObjCmd --
1866 *
1867 * Implements "ns_proxy" wrapper.
1868 *
1869 * Results:
1870 * Standard Tcl result.
1871 *
1872 * Side effects:
1873 * None.
1874 *
1875 *----------------------------------------------------------------------
1876 */
1877
1878static int
1879ProxyObjCmd(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1880{
1881 InterpData *idataPtr = data;
1882 Pool *poolPtr;
1883 Proxy *proxyPtr;
1884 Err err;
1885 int opt, result = TCL_OK0;
1886 const char *proxyId;
1887 Tcl_HashEntry *hPtr;
1888 Tcl_HashSearch search;
1889 Tcl_Obj *listObj;
1890
1891 static const char *opts[] = {
1892 "active", "cleanup", "clear", "configure", "eval",
1893 "free", "get", "handles", "ping", "pools", "put",
1894 "recv", "release", "send", "stats", "stop", "wait",
1895 NULL((void*)0)
1896 };
1897 enum {
1898 PActiveIdx, PCleanupIdx, PClearIdx, PConfigureIdx, PEvalIdx,
1899 PFreeIdx, PGetIdx, PHandlesIdx, PPingIdx, PPoolsIdx, PPutIdx,
1900 PRecvIdx, PReleaseIdx, PSendIdx, PStatsIdx, PStopIdx, PWaitIdx
1901 };
1902
1903 if (objc < 2) {
1904 Tcl_WrongNumArgs(interp, 1, objv, "option ?args?");
1905 return TCL_ERROR1;
1906 }
1907 if (Tcl_GetIndexFromObj(interp, objv[1], opts, "option", 0,Tcl_GetIndexFromObjStruct(interp, objv[1], opts, sizeof(char *
), "option", 0, &opt)
1908 &opt)Tcl_GetIndexFromObjStruct(interp, objv[1], opts, sizeof(char *
), "option", 0, &opt)
!= TCL_OK0) {
1909 return TCL_ERROR1;
1910 }
1911
1912 switch (opt) {
1913 case PReleaseIdx: NS_FALL_THROUGH((void)0); /* fall through */
1914 case PPutIdx: NS_FALL_THROUGH((void)0); /* fall through */
1915 case PPingIdx:
1916 if (objc != 3) {
1917 Tcl_WrongNumArgs(interp, 2, objv, "handle");
1918 result = TCL_ERROR1;
1919 } else {
1920 proxyId = Tcl_GetString(objv[2]);
1921 proxyPtr = GetProxy(proxyId, idataPtr);
1922 if (proxyPtr == NULL((void*)0)) {
1923 Ns_TclPrintfResult(interp, "no such handle: %s", proxyId);
1924 result = TCL_ERROR1;
1925 } else if (opt == PPutIdx || opt == PReleaseIdx) {
1926 result = ReleaseProxy(interp, proxyPtr);
1927 } else /* opt == PPingIdx */ {
1928 result = Eval(interp, proxyPtr, NULL((void*)0), NULL((void*)0));
1929 }
1930 }
1931 break;
1932
1933 case PConfigureIdx:
1934 result = ConfigureObjCmd(data, interp, objc, objv);
1935 break;
1936
1937 case PCleanupIdx:
1938 if (objc != 2) {
1939 Tcl_WrongNumArgs(interp, 2, objv, NULL((void*)0));
1940 result = TCL_ERROR1;
1941 } else {
1942 ReleaseHandles(interp, idataPtr);
1943 }
1944 break;
1945
1946 case PGetIdx:
1947 result = GetObjCmd(data, interp, objc, objv);
1948 break;
1949
1950 case PSendIdx:
1951 if (objc != 4) {
1952 Tcl_WrongNumArgs(interp, 2, objv, "handle script");
1953 result = TCL_ERROR1;
1954 } else {
1955 proxyId = Tcl_GetString(objv[2]);
1956 proxyPtr = GetProxy(proxyId, idataPtr);
1957 if (proxyPtr == NULL((void*)0)) {
1958 Ns_TclPrintfResult(interp, "no such handle: %s", proxyId);
1959 result = TCL_ERROR1;
1960 } else {
1961 err = Send(interp, proxyPtr, Tcl_GetString(objv[3]));
1962 result = (err == ENone) ? TCL_OK0 : TCL_ERROR1;
1963 }
1964 }
1965 break;
1966
1967 case PWaitIdx:
1968 if (objc != 3 && objc != 4) {
1969 Tcl_WrongNumArgs(interp, 2, objv, "handle ?timeout?");
1970 result = TCL_ERROR1;
1971 } else {
1972 Ns_Time *timeoutPtr = NULL((void*)0);
1973
1974 proxyId = Tcl_GetString(objv[2]);
1975 proxyPtr = GetProxy(proxyId, idataPtr);
1976 if (proxyPtr == NULL((void*)0)) {
1977 Ns_TclPrintfResult(interp, "no such handle: %s", proxyId);
1978 result = TCL_ERROR1;
1979 } else if (objc > 3 && Ns_TclGetTimePtrFromObj(interp, objv[3], &timeoutPtr) != TCL_OK0) {
1980 result = TCL_ERROR1;
1981 }
1982 if (result == TCL_OK0) {
1983 err = Wait(interp, proxyPtr, timeoutPtr);
1984 result = (err == ENone) ? TCL_OK0 : TCL_ERROR1;
1985 }
1986 }
1987 break;
1988
1989 case PRecvIdx:
1990 if (objc != 3) {
1991 Tcl_WrongNumArgs(interp, 2, objv, "handle");
1992 result = TCL_ERROR1;
1993 } else {
1994 proxyId = Tcl_GetString(objv[2]);
1995 proxyPtr = GetProxy(proxyId, idataPtr);
1996 if (proxyPtr == NULL((void*)0)) {
1997 Ns_TclPrintfResult(interp, "no such handle: %s", proxyId);
1998 result = TCL_ERROR1;
1999 } else {
2000 err = Recv(interp, proxyPtr, &result);
2001 result = (err == ENone) ? result : TCL_ERROR1;
2002 Ns_Log(Debug, "Receive calls GetStats");
2003
2004 GetStats(proxyPtr);
2005 }
2006 }
2007 break;
2008
2009 case PEvalIdx:
2010 if (objc != 4 && objc != 5) {
2011 Tcl_WrongNumArgs(interp, 2, objv, "handle script");
2012 result = TCL_ERROR1;
2013 } else {
2014 Ns_Time *timeoutPtr = NULL((void*)0);
2015
2016 proxyId = Tcl_GetString(objv[2]);
2017 proxyPtr = GetProxy(proxyId, idataPtr);
2018 if (proxyPtr == NULL((void*)0)) {
2019 Ns_TclPrintfResult(interp, "no such handle: %s", proxyId);
2020 result = TCL_ERROR1;
2021 } else if (objc > 4 && Ns_TclGetTimePtrFromObj(interp, objv[4], &timeoutPtr) != TCL_OK0) {
2022 result = TCL_ERROR1;
2023 }
2024 if (result == TCL_OK0) {
2025 result = Eval(interp, proxyPtr, Tcl_GetString(objv[3]), timeoutPtr);
2026 }
2027 }
2028 break;
2029
2030 case PFreeIdx:
2031 if (objc != 3) {
2032 Tcl_WrongNumArgs(interp, 2, objv, "pool");
2033 result = TCL_ERROR1;
2034 } else {
2035 listObj = Tcl_NewListObj(0, NULL((void*)0));
2036 poolPtr = GetPool(Tcl_GetString(objv[2]), idataPtr);
2037 Ns_MutexLock(&poolPtr->lock);
2038 proxyPtr = poolPtr->firstPtr;
2039 while (proxyPtr != NULL((void*)0)) {
2040 Tcl_ListObjAppendElement(interp, listObj, StringObj(proxyPtr->id));
2041 proxyPtr = proxyPtr->nextPtr;
2042 }
2043 Ns_MutexUnlock(&poolPtr->lock);
2044 Tcl_SetObjResult(interp, listObj);
2045 }
2046 break;
2047
2048 case PHandlesIdx:
2049 if (objc == 3) {
2050 poolPtr = GetPool(Tcl_GetString(objv[2]), idataPtr);
2051 } else {
2052 poolPtr = NULL((void*)0);
2053 }
2054 listObj = Tcl_NewListObj(0, NULL((void*)0));
2055 hPtr = Tcl_FirstHashEntry(&idataPtr->ids, &search);
2056 while (hPtr != NULL((void*)0)) {
2057 proxyPtr = (Proxy *)Tcl_GetHashValue(hPtr)((hPtr)->clientData);
2058 if (poolPtr == NULL((void*)0) || poolPtr == proxyPtr->poolPtr) {
2059 Tcl_ListObjAppendElement(interp, listObj, StringObj(proxyPtr->id));
2060 }
2061 hPtr = Tcl_NextHashEntry(&search);
2062 }
2063 Tcl_SetObjResult(interp, listObj);
2064 break;
2065
2066 case PActiveIdx:
2067 if (objc < 3 || objc > 4) {
2068 Tcl_WrongNumArgs(interp, 2, objv, "pool ?handle?");
2069 result = TCL_ERROR1;
2070 } else {
2071 poolPtr = GetPool(Tcl_GetString(objv[2]), idataPtr);
2072 proxyId = (objc >= 4) ? Tcl_GetString(objv[3]) : NULL((void*)0);
2073 Ns_MutexLock(&plock);
2074 Ns_MutexLock(&poolPtr->lock);
2075 proxyPtr = poolPtr->runPtr;
2076 while (proxyPtr != NULL((void*)0)) {
2077 if (proxyId == NULL((void*)0) || STREQ(proxyId, proxyPtr->id)(((*(proxyId)) == (*(proxyPtr->id))) && (strcmp((proxyId
),(proxyPtr->id)) == 0))
) {
2078 FmtActiveProxy(interp, proxyPtr);
2079 }
2080 proxyPtr = proxyPtr->runPtr;
2081 }
2082 Ns_MutexUnlock(&poolPtr->lock);
2083 Ns_MutexUnlock(&plock);
2084 }
2085 break;
2086
2087 case PStopIdx:
2088 result = StopObjCmd(data, interp, objc, objv);
2089 break;
2090
2091 case PClearIdx:
2092 result = ClearObjCmd(data, interp, objc, objv);
2093 break;
2094
2095 case PPoolsIdx:
2096 listObj = Tcl_NewListObj(0, NULL((void*)0));
2097 Ns_MutexLock(&plock);
2098 hPtr = Tcl_FirstHashEntry(&pools, &search);
2099 while (hPtr != NULL((void*)0)) {
2100 poolPtr = (Pool *)Tcl_GetHashValue(hPtr)((hPtr)->clientData);
2101 Tcl_ListObjAppendElement(interp, listObj, StringObj(poolPtr->name));
2102 hPtr = Tcl_NextHashEntry(&search);
2103 }
2104 Ns_MutexUnlock(&plock);
2105 Tcl_SetObjResult(interp, listObj);
2106 break;
2107
2108 case PStatsIdx:
2109 result = StatsObjCmd(data, interp, objc, objv);
2110 break;
2111 }
2112
2113 return result;
2114}
2115
2116
2117/*
2118 *----------------------------------------------------------------------
2119 *
2120 * ConfigureObjCmd --
2121 *
2122 * Implements "ns_proxy configure".
2123 *
2124 * Results:
2125 * Standard Tcl result.
2126 *
2127 * Side effects:
2128 * Will update one or more config options.
2129 *
2130 *----------------------------------------------------------------------
2131 */
2132
2133static int
2134ConfigureObjCmd(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
2135{
2136 InterpData *idataPtr = data;
2137 Pool *poolPtr;
2138 Proxy *proxyPtr;
2139 int flag = 0, n, result = TCL_OK0, reap = 0;
2140
2141 static const char *flags[] = {
2142 "-init", "-reinit", "-maxslaves", "-exec", "-env",
2143 "-gettimeout", "-evaltimeout", "-sendtimeout", "-recvtimeout",
2144 "-waittimeout", "-idletimeout", "-logminduration", "-maxruns",
2145 "-maxworkers", NULL((void*)0)
2146 };
2147 enum {
2148 CInitIdx, CReinitIdx, CMaxslaveIdx, CExecIdx, CEnvIdx,
2149 CGetIdx, CEvalIdx, CSendIdx, CRecvIdx,
2150 CWaitIdx, CIdleIdx, CLogmindurationIdx, CMaxrunsIdx,
2151 CMaxworkerIdx
2152 };
2153
2154 if (objc < 3) {
2155 Tcl_WrongNumArgs(interp, 2, objv, "pool ?opt? ?val? ?opt val?...");
2156 return TCL_ERROR1;
2157 }
2158
2159 poolPtr = GetPool(Tcl_GetString(objv[2]), idataPtr);
2160 Ns_MutexLock(&poolPtr->lock);
2161 if (objc == 4) {
2162 if (Tcl_GetIndexFromObj(interp, objv[3], flags, "flags", 0,Tcl_GetIndexFromObjStruct(interp, objv[3], flags, sizeof(char
*), "flags", 0, &flag)
2163 &flag)Tcl_GetIndexFromObjStruct(interp, objv[3], flags, sizeof(char
*), "flags", 0, &flag)
!= TCL_OK0) {
2164 result = TCL_ERROR1;
2165 goto err;
2166 }
2167 } else if (objc > 4) {
2168 int i;
2169 const char *str;
2170
2171 for (i = 3; i < (objc - 1); ++i) {
2172 if (Tcl_GetIndexFromObj(interp, objv[i], flags, "flags", 0,Tcl_GetIndexFromObjStruct(interp, objv[i], flags, sizeof(char
*), "flags", 0, &flag)
2173 &flag)Tcl_GetIndexFromObjStruct(interp, objv[i], flags, sizeof(char
*), "flags", 0, &flag)
) {
2174 result = TCL_ERROR1;
2175 goto err;
2176 }
2177 ++i;
2178 str = Tcl_GetString(objv[i]);
2179 switch (flag) {
2180 case CEvalIdx: NS_FALL_THROUGH((void)0); /* fall through */
2181 case CGetIdx: NS_FALL_THROUGH((void)0); /* fall through */
2182 case CIdleIdx: NS_FALL_THROUGH((void)0); /* fall through */
2183 case CLogmindurationIdx: NS_FALL_THROUGH((void)0); /* fall through */
2184 case CRecvIdx: NS_FALL_THROUGH((void)0); /* fall through */
2185 case CSendIdx: NS_FALL_THROUGH((void)0); /* fall through */
2186 case CWaitIdx: {
2187 Ns_Time timeout;
2188
2189 if (Ns_TclGetTimeFromObj(interp, objv[i], &timeout) != TCL_OK0) {
2190 result = TCL_ERROR1;
2191 goto err;
2192 }
2193 switch (flag) {
2194 case CRecvIdx:
2195 poolPtr->conf.trecv = timeout;
2196 break;
2197 case CSendIdx:
2198 poolPtr->conf.tsend = timeout;
2199 break;
2200 case CEvalIdx:
2201 poolPtr->conf.teval = timeout;
2202 break;
2203 case CWaitIdx:
2204 poolPtr->conf.twait = timeout;
2205 break;
2206 case CGetIdx:
2207 poolPtr->conf.tget = timeout;
2208 break;
2209 case CLogmindurationIdx:
2210 poolPtr->conf.logminduration = timeout;
2211 break;
2212 case CIdleIdx:
2213 {
2214 Ns_Time minIdle = { MIN_IDLE_TIMEOUT_SEC10, 0 };
2215
2216 poolPtr->conf.tidle = timeout;
2217 if (Ns_DiffTime(&poolPtr->conf.tidle, &minIdle, NULL((void*)0)) == -1) {
2218 poolPtr->conf.tidle = minIdle;
2219 }
2220 proxyPtr = poolPtr->firstPtr;
2221 while (proxyPtr != NULL((void*)0)) {
2222 if (proxyPtr->workerPtr != NULL((void*)0)) {
2223 SetExpire(proxyPtr->workerPtr, &proxyPtr->conf.tidle);
2224 }
2225 proxyPtr = proxyPtr->nextPtr;
2226 }
2227 reap = 1;
2228 break;
2229 }
2230 }
2231 break;
2232 }
2233
2234 case CMaxslaveIdx: NS_FALL_THROUGH((void)0); /* fall through */
2235 case CMaxworkerIdx: NS_FALL_THROUGH((void)0); /* fall through */
2236 case CMaxrunsIdx:
2237 if (Tcl_GetIntFromObj(interp, objv[i], &n) != TCL_OK0) {
2238 result = TCL_ERROR1;
2239 goto err;
2240 }
2241 if (n < 0) {
2242 Ns_TclPrintfResult(interp, "invalid %s: %s",
2243 flags[flag], str);
2244 result = TCL_ERROR1;
2245 goto err;
2246 }
2247 switch (flag) {
2248 case CMaxslaveIdx: NS_FALL_THROUGH((void)0); /* fall through */
2249 case CMaxworkerIdx:
2250 poolPtr->maxworker = n;
2251 reap = 1;
2252 break;
2253 case CMaxrunsIdx:
2254 poolPtr->conf.maxruns = n;
2255 break;
2256 }
2257 break;
2258 case CInitIdx:
2259 SetOpt(str, &poolPtr->init);
2260 break;
2261 case CReinitIdx:
2262 SetOpt(str, &poolPtr->reinit);
2263 break;
2264 case CExecIdx:
2265 SetOpt(str, &poolPtr->exec);
2266 break;
2267 case CEnvIdx:
2268 if (poolPtr->env) {
2269 Ns_SetFree(poolPtr->env);
2270 }
2271 poolPtr->env = Ns_SetCopy(Ns_TclGetSet(interp, str));
2272 break;
2273 }
2274 }
2275
2276 /*
2277 * Assure number of idle and used proxies always
2278 * match the maximum number of configured ones.
2279 */
2280
2281 while ((poolPtr->nfree + poolPtr->nused) < poolPtr->maxworker) {
2282 proxyPtr = CreateProxy(poolPtr);
2283 proxyPtr->nextPtr = poolPtr->firstPtr;
2284 poolPtr->firstPtr = proxyPtr;
2285 poolPtr->nfree++;
2286 }
2287 }
2288
2289 /*
2290 * Construct command result
2291 */
2292 Tcl_ResetResult(interp);
2293
2294 if (objc == 3) {
2295 Tcl_Obj *listObj = Tcl_NewListObj(0, NULL((void*)0));
2296
2297 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj(flags[CEnvIdx], -1));
2298 if (poolPtr->env != NULL((void*)0)) {
2299 if (unlikely(Ns_TclEnterSet(interp, poolPtr->env, NS_TCL_SET_DYNAMIC) != TCL_OK)(__builtin_expect((Ns_TclEnterSet(interp, poolPtr->env, NS_TCL_SET_DYNAMIC
) != 0), 0))
) {
2300 result = TCL_ERROR1;
2301 } else {
2302 /*
2303 * Ns_TclEnterSet() sets the result
2304 */
2305 }
2306 }
2307
2308 if (result == TCL_OK0) {
2309 Tcl_ListObjAppendElement(interp, listObj, Tcl_GetObjResult(interp));
2310 AppendObj(listObj, flags[CExecIdx], StringObj(poolPtr->exec));
2311 AppendObj(listObj, flags[CInitIdx], StringObj(poolPtr->init));
2312 AppendObj(listObj, flags[CReinitIdx], StringObj(poolPtr->reinit));
2313 AppendObj(listObj, flags[CMaxworkerIdx], Tcl_NewIntObj(poolPtr->maxworker));
2314 AppendObj(listObj, flags[CMaxrunsIdx], Tcl_NewIntObj(poolPtr->conf.maxruns));
2315 AppendObj(listObj, flags[CGetIdx], Ns_TclNewTimeObj(&poolPtr->conf.tget));
2316 AppendObj(listObj, flags[CEvalIdx], Ns_TclNewTimeObj(&poolPtr->conf.teval));
2317 AppendObj(listObj, flags[CSendIdx], Ns_TclNewTimeObj(&poolPtr->conf.tsend));
2318 AppendObj(listObj, flags[CRecvIdx], Ns_TclNewTimeObj(&poolPtr->conf.trecv));
2319 AppendObj(listObj, flags[CWaitIdx], Ns_TclNewTimeObj(&poolPtr->conf.twait));
2320 AppendObj(listObj, flags[CIdleIdx], Ns_TclNewTimeObj(&poolPtr->conf.tidle));
2321 AppendObj(listObj, flags[CLogmindurationIdx], Ns_TclNewTimeObj(&poolPtr->conf.logminduration));
2322 Tcl_SetObjResult(interp, listObj);
2323 }
2324
2325 } else if (objc == 4) {
2326 switch (flag) {
2327 case CExecIdx: Tcl_SetObjResult(interp, StringObj(poolPtr->exec));
2328 break;
2329 case CInitIdx: Tcl_SetObjResult(interp, StringObj(poolPtr->init));
2330 break;
2331 case CReinitIdx: Tcl_SetObjResult(interp, StringObj(poolPtr->reinit));
2332 break;
2333 case CMaxslaveIdx: NS_FALL_THROUGH((void)0); /* fall through */
2334 case CMaxworkerIdx: Tcl_SetObjResult(interp, Tcl_NewIntObj(poolPtr->maxworker));
2335 break;
2336 case CMaxrunsIdx: Tcl_SetObjResult(interp, Tcl_NewIntObj(poolPtr->conf.maxruns));
2337 break;
2338 case CGetIdx: Tcl_SetObjResult(interp, Ns_TclNewTimeObj(&poolPtr->conf.tget));
2339 break;
2340 case CEvalIdx: Tcl_SetObjResult(interp, Ns_TclNewTimeObj(&poolPtr->conf.teval));
2341 break;
2342 case CSendIdx: Tcl_SetObjResult(interp, Ns_TclNewTimeObj(&poolPtr->conf.tsend));
2343 break;
2344 case CRecvIdx: Tcl_SetObjResult(interp, Ns_TclNewTimeObj(&poolPtr->conf.trecv));
2345 break;
2346 case CWaitIdx: Tcl_SetObjResult(interp, Ns_TclNewTimeObj(&poolPtr->conf.twait));
2347 break;
2348 case CIdleIdx: Tcl_SetObjResult(interp, Ns_TclNewTimeObj(&poolPtr->conf.tidle));
2349 break;
2350 case CLogmindurationIdx: Tcl_SetObjResult(interp, Ns_TclNewTimeObj(&poolPtr->conf.logminduration));
2351 break;
2352 case CEnvIdx:
2353 if (poolPtr->env) {
2354 /*
2355 * Ns_TclEnterSet() sets the result
2356 */
2357 if (unlikely(Ns_TclEnterSet(interp, poolPtr->env, NS_TCL_SET_DYNAMIC) != TCL_OK)(__builtin_expect((Ns_TclEnterSet(interp, poolPtr->env, NS_TCL_SET_DYNAMIC
) != 0), 0))
) {
2358 result = TCL_ERROR1;
2359 }
2360 } else {
2361 /*
2362 * The result is empty.
2363 */
2364 }
2365 break;
2366 }
2367 } else if (objc == 5) {
2368 Tcl_SetObjResult(interp, objv[4]);
2369 }
2370
2371 err:
2372 Ns_MutexUnlock(&poolPtr->lock);
2373
2374 /*
2375 * Optionally, wake up reaper thread
2376 * to collect closing proxies or to
2377 * enforce pool size constraints.
2378 */
2379
2380 if (reap != 0) {
2381 ReapProxies();
2382 }
2383
2384 return result;
2385}
2386
2387
2388static void
2389SetOpt(const char *str, char const **optPtr)
2390{
2391 NS_NONNULL_ASSERT(str != NULL)((void) (0));
2392 NS_NONNULL_ASSERT(optPtr != NULL)((void) (0));
2393
2394 if (*optPtr != NULL((void*)0)) {
2395 ns_free((char*)*optPtr);
2396 }
2397 if (*str != '\0') {
2398 *optPtr = ns_strdup(str);
2399 } else {
2400 *optPtr = NULL((void*)0);
2401 }
2402}
2403
2404static Tcl_Obj*
2405StringObj(const char* chars) {
2406 Tcl_Obj *resultObj;
2407
2408 if (chars != NULL((void*)0)) {
2409 resultObj = Tcl_NewStringObj(chars, -1);
2410 } else {
2411 resultObj = Tcl_NewStringObj("", 0);
2412 }
2413 return resultObj;
2414}
2415
2416static void
2417AppendObj(Tcl_Obj *listObj, const char *flag, Tcl_Obj *obj)
2418{
2419 NS_NONNULL_ASSERT(listObj != NULL)((void) (0));
2420 NS_NONNULL_ASSERT(flag != NULL)((void) (0));
2421
2422 Tcl_ListObjAppendElement(NULL((void*)0), listObj, StringObj(flag));
2423 Tcl_ListObjAppendElement(NULL((void*)0), listObj, obj);
2424}
2425
2426
2427/*
2428 *----------------------------------------------------------------------
2429 *
2430 * GetObjCmd --
2431 *
2432 * Implements "ns_proxy get".
2433 *
2434 * Results:
2435 * Standard Tcl result.
2436 *
2437 * Side effects:
2438 * May allocate one or more handles.
2439 *
2440 *----------------------------------------------------------------------
2441 */
2442
2443static int
2444GetObjCmd(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
2445{
2446 InterpData *idataPtr = data;
2447 Proxy *proxyPtr, *firstPtr;
2448 Tcl_HashEntry *cntPtr, *idPtr;
2449 int isNew, nwant = 1, result = TCL_OK0;
2450 Ns_Time *timeoutPtr = NULL((void*)0);
2451 Err err;
2452 Pool *poolPtr;
2453 Ns_ObjvSpec lopts[] = {
2454 {"-timeout", Ns_ObjvTime, &timeoutPtr, NULL((void*)0)},
2455 {"-handles", Ns_ObjvInt, &nwant, NULL((void*)0)},
2456 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
2457 };
2458
2459 if (objc < 3 || (objc % 2) != 1) {
2460 Tcl_WrongNumArgs(interp, 2, objv, "pool ?-opt val -opt val ...?");
2461 return TCL_ERROR1;
2462 }
2463
2464 assert(idataPtr != NULL)((void) (0));
2465 poolPtr = GetPool(Tcl_GetString(objv[2]), idataPtr);
2466 assert(poolPtr != NULL)((void) (0));
2467
2468 cntPtr = Tcl_CreateHashEntry(&idataPtr->cnts, (char *) poolPtr, &isNew)(*((&idataPtr->cnts)->createProc))(&idataPtr->
cnts, (const char *)((char *) poolPtr), &isNew)
;
2469 if ((intptr_t) Tcl_GetHashValue(cntPtr)((cntPtr)->clientData) > 0) {
2470 err = EDeadlock;
2471 goto errout;
2472 }
2473
2474 if (Ns_ParseObjv(lopts, NULL((void*)0), interp, 3, objc, objv) != NS_OK) {
2475 return TCL_ERROR1;
2476 }
2477
2478 if (timeoutPtr == NULL((void*)0)) {
2479 Ns_MutexLock(&poolPtr->lock);
2480 timeoutPtr = &poolPtr->conf.tget;
2481 Ns_MutexUnlock(&poolPtr->lock);
2482 }
2483
2484 /*
2485 * Get some number of proxies from the pool
2486 */
2487
2488 err = PopProxy(poolPtr, &firstPtr, nwant, timeoutPtr);
2489 if (err != 0) {
2490 errout:
2491 Ns_TclPrintfResult(interp, "could not allocate from pool \"%s\": %s",
2492 poolPtr->name, errMsg[err]);
2493 ProxyError(interp, err);
2494 return TCL_ERROR1;
2495 }
2496
2497 /*
2498 * Set total owned count and create handle ids.
2499 */
2500
2501 Tcl_SetHashValue(cntPtr, INT2PTR(nwant))((cntPtr)->clientData = (ClientData) (((void *)(intptr_t)(
nwant))))
;
2502 proxyPtr = firstPtr;
2503 while (proxyPtr != NULL((void*)0)) {
2504 idPtr = Tcl_CreateHashEntry(&idataPtr->ids, proxyPtr->id, &isNew)(*((&idataPtr->ids)->createProc))(&idataPtr->
ids, (const char *)(proxyPtr->id), &isNew)
;
2505 if (isNew == 0) {
2506 Ns_Fatal("nsproxy: duplicate proxy entry");
2507 }
2508 Tcl_SetHashValue(idPtr, proxyPtr)((idPtr)->clientData = (ClientData) (proxyPtr));
2509 proxyPtr->cntPtr = cntPtr;
2510 proxyPtr->idPtr = idPtr;
2511 proxyPtr = proxyPtr->nextPtr;
2512 }
2513
2514 /*
2515 * Check each proxy for valid connection.
2516 */
2517
2518 err = ENone;
2519 proxyPtr = firstPtr;
2520 while (err == ENone && proxyPtr != NULL((void*)0)) {
2521 err = CheckProxy(interp, proxyPtr);
2522 proxyPtr = proxyPtr->nextPtr;
2523 }
2524 if (err != ENone) {
2525 while ((proxyPtr = firstPtr) != NULL((void*)0)) {
2526 firstPtr = proxyPtr->nextPtr;
2527 PushProxy(proxyPtr);
2528 }
2529 result = TCL_ERROR1;
2530 }
2531
2532 if (result == TCL_OK0) {
2533 /*
2534 * Generate accessor commands for the returned proxies.
2535 */
2536 Tcl_Obj *listObj = Tcl_NewListObj(0, NULL((void*)0));
2537
2538 proxyPtr = firstPtr;
2539 while (proxyPtr != NULL((void*)0)) {
2540 proxyPtr->cmdToken = Tcl_CreateObjCommand(interp, proxyPtr->id,
2541 RunProxyObjCmd, proxyPtr,
2542 DelProxyProc);
2543 if (proxyPtr->cmdToken == NULL((void*)0)) {
2544 result = TCL_ERROR1;
2545 break;
2546 }
2547 proxyPtr->interp = interp;
2548 Tcl_ListObjAppendElement(interp, listObj, StringObj(proxyPtr->id));
2549 proxyPtr = proxyPtr->nextPtr;
2550 }
2551
2552 if (result == TCL_OK0) {
2553 Tcl_SetObjResult(interp, listObj);
2554 } else {
2555 Tcl_DecrRefCount(listObj)do { Tcl_Obj *_objPtr = (listObj); if (_objPtr->refCount--
<= 1) { TclFreeObj(_objPtr); } } while(0)
;
2556 }
2557 }
2558
2559 return result;
2560}
2561
2562
2563/*
2564 *----------------------------------------------------------------------
2565 *
2566 * PopProxy --
2567 *
2568 * Pops number of free proxies from the pool.
2569 *
2570 * Results:
2571 * Error message or NULL if all went fine..
2572 *
2573 * Side effects:
2574 * None.
2575 *
2576 *----------------------------------------------------------------------
2577 */
2578
2579static Err
2580PopProxy(Pool *poolPtr, Proxy **proxyPtrPtr, int nwant, const Ns_Time *timePtr)
2581{
2582 Proxy *proxyPtr;
2583 Err err;
2584 Ns_ReturnCode status = NS_OK;
2585 Ns_Time waitTimeout;
2586
2587 NS_NONNULL_ASSERT(poolPtr != NULL)((void) (0));
2588 NS_NONNULL_ASSERT(proxyPtrPtr != NULL)((void) (0));
2589
2590 if (timePtr != NULL((void*)0)) {
2591 Ns_GetTime(&waitTimeout);
2592 Ns_IncrTime(&waitTimeout, timePtr->sec, timePtr->usec);
2593 }
2594
2595 Ns_MutexLock(&poolPtr->lock);
2596 while (status == NS_OK && poolPtr->waiting > 0) {
2597 if (timePtr != NULL((void*)0)) {
2598 status = Ns_CondTimedWait(&poolPtr->cond, &poolPtr->lock, &waitTimeout);
2599 } else {
2600 Ns_CondWait(&poolPtr->cond, &poolPtr->lock);
2601 }
2602 }
2603 if (status != NS_OK) {
2604 err = EGetTimeout;
2605 } else {
2606 poolPtr->waiting = 1;
2607 while (status == NS_OK
2608 && poolPtr->nfree < nwant && poolPtr->maxworker >= nwant) {
2609 if (timePtr != NULL((void*)0)) {
2610 status = Ns_CondTimedWait(&poolPtr->cond, &poolPtr->lock,
2611 &waitTimeout);
2612 } else {
2613 Ns_CondWait(&poolPtr->cond, &poolPtr->lock);
2614 }
2615 }
2616 if (status != NS_OK) {
2617 err = EGetTimeout;
2618 } else if (poolPtr->maxworker == 0 || poolPtr->maxworker < nwant) {
2619 err = ERange;
2620 } else {
2621 int i;
2622
2623 poolPtr->nfree -= nwant;
2624 poolPtr->nused += nwant;
2625
2626 for (i = 0, *proxyPtrPtr = NULL((void*)0); i < nwant; ++i) {
2627 proxyPtr = poolPtr->firstPtr;
2628 poolPtr->firstPtr = proxyPtr->nextPtr;
2629 proxyPtr->nextPtr = *proxyPtrPtr;
2630 *proxyPtrPtr = proxyPtr;
2631 proxyPtr->conf = poolPtr->conf;
2632 }
2633 err = ENone;
2634 }
2635 poolPtr->waiting = 0;
2636 Ns_CondBroadcast(&poolPtr->cond);
2637 }
2638 Ns_MutexUnlock(&poolPtr->lock);
2639
2640 return err;
2641}
2642
2643
2644/*
2645 *----------------------------------------------------------------------
2646 *
2647 * FmtActiveProxy --
2648 *
2649 * Fills in the interp result with list of proxy values..
2650 *
2651 * Results:
2652 * None.
2653 *
2654 * Side effects:
2655 * None.
2656 *
2657 *----------------------------------------------------------------------
2658 */
2659
2660static void
2661FmtActiveProxy(Tcl_Interp *interp, const Proxy *proxyPtr)
2662{
2663 Tcl_DString ds;
2664
2665 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
2666 NS_NONNULL_ASSERT(proxyPtr != NULL)((void) (0));
2667
2668 Tcl_DStringInit(&ds);
2669 Tcl_DStringGetResult(interp, &ds);
2670
2671 Tcl_DStringStartSublist(&ds);
2672 Ns_DStringPrintf(&ds, "handle %s slave %ld start " NS_TIME_FMT"%" "l" "d" ".%06ld" " script",
2673 proxyPtr->id,
2674 (long) ((proxyPtr->workerPtr != NULL((void*)0)) ? proxyPtr->workerPtr->pid : 0),
2675 (int64_t) proxyPtr->when.sec,
2676 proxyPtr->when.usec);
2677
2678 Tcl_DStringAppendElement(&ds, Tcl_DStringValue(&proxyPtr->in)((&proxyPtr->in)->string) + sizeof(Req));
2679 Tcl_DStringEndSublist(&ds);
2680
2681 Tcl_DStringResult(interp, &ds);
2682}
2683
2684
2685/*
2686 *----------------------------------------------------------------------
2687 *
2688 * GetPool --
2689 *
2690 * Get a pool by name.
2691 *
2692 * Results:
2693 * Pool pointer.
2694 *
2695 * Side effects:
2696 * Will update given poolPtrPtr with pointer to Pool.
2697 *
2698 *----------------------------------------------------------------------
2699 */
2700
2701static Pool*
2702GetPool(const char *poolName, const InterpData *idataPtr)
2703{
2704 Tcl_HashEntry *hPtr;
2705 Pool *poolPtr;
2706 Proxy *proxyPtr;
2707 int isNew;
2708
2709 NS_NONNULL_ASSERT(poolName != NULL)((void) (0));
2710
2711 Ns_MutexLock(&plock);
2712 hPtr = Tcl_CreateHashEntry(&pools, poolName, &isNew)(*((&pools)->createProc))(&pools, (const char *)(poolName
), &isNew)
;
2713 if (isNew == 0) {
2714 poolPtr = (Pool *)Tcl_GetHashValue(hPtr)((hPtr)->clientData);
2715
2716 } else {
2717 poolPtr = ns_calloc(1u, sizeof(Pool));
2718 Tcl_SetHashValue(hPtr, poolPtr)((hPtr)->clientData = (ClientData) (poolPtr));
2719 poolPtr->name = Tcl_GetHashKey(&pools, hPtr)((void *) (((&pools)->keyType == (1) || (&pools)->
keyType == (-1)) ? (hPtr)->key.oneWordValue : (hPtr)->key
.string))
;
2720
2721 /*
2722 * If we have idataPtr with a "server" and "module" we can fill into
2723 * the pool structure the values from the config file.
2724 */
2725 if (idataPtr == NULL((void*)0) || idataPtr->server == NULL((void*)0) || idataPtr->module == NULL((void*)0)) {
2726 /*
2727 * Just default values. When is this path taken?
2728 */
2729 SetOpt(Tcl_DStringValue(&defexec)((&defexec)->string), &poolPtr->exec);
2730
2731 /*
2732 * Just fill out non-null values.
2733 */
2734 poolPtr->conf.tsend.sec = 5;
2735 poolPtr->conf.trecv.sec = 5;
2736 poolPtr->conf.twait.sec = 1;
2737 poolPtr->conf.tidle.sec = 5 * 60;
2738 poolPtr->maxworker = 8;
2739 poolPtr->conf.logminduration.sec = 1;
2740
2741 } else {
2742 const char *exec, *path;
2743
2744 path = Ns_ConfigSectionPath(NULL((void*)0), idataPtr->server, idataPtr->module, (char *)0L);
2745 exec = Ns_ConfigGetValue(path, "exec");
2746 if (exec != NULL((void*)0)) {
2747 SetOpt(exec, &poolPtr->exec);
2748 } else {
2749 SetOpt(Tcl_DStringValue(&defexec)((&defexec)->string), &poolPtr->exec);
2750 }
2751 Ns_ConfigTimeUnitRange(path, "gettimeout",
2752 "0ms", 0, 0, INT_MAX2147483647, 0,
2753 &poolPtr->conf.tget);
2754
2755 Ns_ConfigTimeUnitRange(path, "evaltimeout",
2756 "0ms", 0, 0, INT_MAX2147483647, 0,
2757 &poolPtr->conf.teval);
2758
2759 Ns_ConfigTimeUnitRange(path, "sendtimeout",
2760 "5s", 0, 0, INT_MAX2147483647, 0,
2761 &poolPtr->conf.tsend);
2762
2763 Ns_ConfigTimeUnitRange(path, "recvtimeout",
2764 "5s", 0, 0, INT_MAX2147483647, 0,
2765 &poolPtr->conf.trecv);
2766
2767 Ns_ConfigTimeUnitRange(path, "waittimeout",
2768 "1s", 0, 0, INT_MAX2147483647, 0,
2769 &poolPtr->conf.twait);
2770
2771 Ns_ConfigTimeUnitRange(path, "idletimeout",
2772 "5m", MIN_IDLE_TIMEOUT_SEC10, 0, INT_MAX2147483647, 0,
2773 &poolPtr->conf.tidle);
2774
2775 {
2776 int max = Ns_ConfigInt(path, "maxworker", -1);
2777 if (max == -1) {
2778 max = Ns_ConfigInt(path, "maxslaves", -1);
2779 }
2780 if (max == -1) {
2781 max = 8;
2782 }
2783 poolPtr->maxworker = max;
2784 }
2785
2786 Ns_ConfigTimeUnitRange(path, "logminduration",
2787 "1s", 0, 0, INT_MAX2147483647, 0,
2788 &poolPtr->conf.logminduration);
2789 }
2790
2791 {
2792 int i;
2793 for (i = 0; i < poolPtr->maxworker; i++) {
2794 proxyPtr = CreateProxy(poolPtr);
2795 proxyPtr->nextPtr = poolPtr->firstPtr;
2796 poolPtr->firstPtr = proxyPtr;
2797 poolPtr->nfree++;
2798 }
2799 }
2800 Ns_CondInit(&poolPtr->cond);
2801 Ns_MutexInit(&poolPtr->lock);
2802 Ns_MutexSetName2(&poolPtr->lock, "nsproxy", poolName);
2803
2804 }
2805 Ns_MutexUnlock(&plock);
2806
2807 return poolPtr;
2808}
2809
2810/*
2811 *----------------------------------------------------------------------
2812 *
2813 * CreateProxy --
2814 *
2815 * Create new proxy handle.
2816 *
2817 * Results:
2818 * Proxy handle.
2819 *
2820 * Side effects:
2821 * Assumes pool lock is held.
2822 *
2823 *----------------------------------------------------------------------
2824 */
2825
2826static Proxy*
2827CreateProxy(Pool *poolPtr)
2828{
2829 Proxy *proxyPtr;
2830 char buf[TCL_INTEGER_SPACE24];
2831 size_t nameLength;
2832 int idLength;
2833
2834 NS_NONNULL_ASSERT(poolPtr != NULL)((void) (0));
2835
2836 idLength = snprintf(buf, sizeof(buf), "%" PRIuPTR, poolPtr->nextid++)__builtin___snprintf_chk (buf, sizeof(buf), 2 - 1, __builtin_object_size
(buf, 2 > 1), "%" "l" "u", poolPtr->nextid++)
;
2837 nameLength = strlen(poolPtr->name);
2838
2839 proxyPtr = ns_calloc(1u, sizeof(Proxy));
2840 proxyPtr->poolPtr = poolPtr;
2841
2842 proxyPtr->id = ns_calloc(1u, strlen(buf) + nameLength + 2u);
2843 memcpy(proxyPtr->id, poolPtr->name, nameLength);
2844 *(proxyPtr->id + nameLength) = '-';
2845 memcpy(proxyPtr->id + nameLength + 1u, buf, (size_t)idLength + 1u);
2846
2847 Tcl_DStringInit(&proxyPtr->in);
2848 Tcl_DStringInit(&proxyPtr->out);
2849
2850 return proxyPtr;
2851}
2852
2853
2854/*
2855 *----------------------------------------------------------------------
2856 *
2857 * GetProxy --
2858 *
2859 * Get a previously allocated proxy handle.
2860 *
2861 * Results:
2862 * Pointer to the proxy.
2863 *
2864 * Side effects:
2865 * Imposes maxruns limit.
2866 *
2867 *----------------------------------------------------------------------
2868 */
2869
2870static Proxy*
2871GetProxy(const char *proxyId, InterpData *idataPtr)
2872{
2873 const Tcl_HashEntry *hPtr;
2874 Proxy *result = NULL((void*)0);
2875
2876 hPtr = Tcl_FindHashEntry(&idataPtr->ids, proxyId)(*((&idataPtr->ids)->findProc))(&idataPtr->ids
, (const char *)(proxyId))
;
2877 if (likely(hPtr != NULL)(__builtin_expect((hPtr != ((void*)0)), 1))) {
2878 result = (Proxy *)Tcl_GetHashValue(hPtr)((hPtr)->clientData);
2879 }
2880
2881 return result;
2882}
2883
2884
2885/*
2886 *----------------------------------------------------------------------
2887 *
2888 * CheckProxy --
2889 *
2890 * Check a proxy, pinging the proc and creating a new worker processes as
2891 * needed.
2892 *
2893 * Results:
2894 * ENone if proxy OK, other error if worker process could not be created.
2895 *
2896 * Side effects:
2897 * None.
2898 *
2899 *----------------------------------------------------------------------
2900 */
2901
2902static Err
2903CheckProxy(Tcl_Interp *interp, Proxy *proxyPtr)
2904{
2905 Err err = ENone;
2906
2907 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
2908 NS_NONNULL_ASSERT(proxyPtr != NULL)((void) (0));
2909
2910 if ((proxyPtr->workerPtr != NULL((void*)0))
2911 && (Eval(interp, proxyPtr, NULL((void*)0), NULL((void*)0)) != TCL_OK0)
2912 ) {
2913 CloseProxy(proxyPtr);
2914 Tcl_ResetResult(interp);
2915 }
2916 if (proxyPtr->workerPtr == NULL((void*)0)) {
2917 err = CreateWorker(interp, proxyPtr);
2918 }
2919
2920 return err;
2921}
2922
2923
2924/*
2925 *----------------------------------------------------------------------
2926 *
2927 * CreateWorker --
2928 *
2929 * Create new proxy worker process
2930 *
2931 * Results:
2932 * ENone if proxy OK, other error if worker could not be created.
2933 *
2934 * Side effects:
2935 *
2936 *
2937 *----------------------------------------------------------------------
2938 */
2939
2940static Err
2941CreateWorker(Tcl_Interp *interp, Proxy *proxyPtr)
2942{
2943 Pool *poolPtr;
2944 Err err;
2945 int init;
2946 Tcl_DString ds;
2947
2948 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
2949 NS_NONNULL_ASSERT(proxyPtr != NULL)((void) (0));
2950
2951 poolPtr = proxyPtr->poolPtr;
2952
2953 Tcl_DStringInit(&ds);
2954 Ns_MutexLock(&poolPtr->lock);
2955 init = proxyPtr->poolPtr->init != NULL((void*)0);
2956 if (init != 0) {
2957 Tcl_DStringAppend(&ds, poolPtr->init, -1);
2958 }
2959 Ns_MutexUnlock(&poolPtr->lock);
2960 proxyPtr->workerPtr = ExecWorker(interp, proxyPtr);
2961 if (proxyPtr->workerPtr == NULL((void*)0)) {
2962 err = EExec;
2963 } else if (init != 0
2964 && (Eval(interp, proxyPtr, Tcl_DStringValue(&ds)((&ds)->string), NULL((void*)0)) != TCL_OK0)
2965 ) {
2966 CloseProxy(proxyPtr);
2967 err = EInit;
2968 } else if (Eval(interp, proxyPtr, NULL((void*)0), NULL((void*)0)) != TCL_OK0) {
2969 CloseProxy(proxyPtr);
2970 err = EInit;
2971 } else {
2972 err = ENone;
2973 Tcl_ResetResult(interp);
2974 }
2975 Tcl_DStringFree(&ds);
2976 if (err != EExec) {
2977 ReapProxies();
2978 }
2979
2980 return err;
2981}
2982
2983
2984/*
2985 *----------------------------------------------------------------------
2986 *
2987 * ResetProxy --
2988 *
2989 * Reset a proxy preparing it for the next request.
2990 *
2991 * Results:
2992 * None.
2993 *
2994 * Side effects:
2995 * None.
2996 *
2997 *----------------------------------------------------------------------
2998 */
2999
3000static void
3001ResetProxy(Proxy *proxyPtr)
3002{
3003 Pool *poolPtr;
3004 Proxy *runPtr, *prevPtr;
3005
3006 NS_NONNULL_ASSERT(proxyPtr)((void) (0));
3007
3008 poolPtr = proxyPtr->poolPtr;
3009 /*
3010 * Non-idle proxies will be closed forcefully
3011 */
3012
3013 if (proxyPtr->state != Idle) {
3014 CloseProxy(proxyPtr);
3015 proxyPtr->state = Idle;
3016 }
3017
3018 /*
3019 * Splice out of the run queue
3020 */
3021
3022 Ns_MutexLock(&poolPtr->lock);
3023 runPtr = prevPtr = poolPtr->runPtr;
3024 while (runPtr != NULL((void*)0) && runPtr != proxyPtr) {
3025 prevPtr = runPtr;
3026 runPtr = runPtr->runPtr;
3027 }
3028 if (runPtr != NULL((void*)0)) {
3029 if (runPtr == poolPtr->runPtr) {
3030 poolPtr->runPtr = runPtr->runPtr;
3031 } else {
3032 prevPtr->runPtr = runPtr->runPtr;
3033 }
3034 } else if (prevPtr != NULL((void*)0)) {
3035 prevPtr->runPtr = NULL((void*)0);
3036 }
3037 Ns_MutexUnlock(&poolPtr->lock);
3038
3039 Tcl_DStringSetLength(&proxyPtr->in, 0);
3040 Tcl_DStringSetLength(&proxyPtr->out, 0);
3041}
3042
3043
3044/*
3045 *----------------------------------------------------------------------
3046 *
3047 * CloseWorker --
3048 *
3049 * Close the given proc handle.
3050 *
3051 * Results:
3052 * None.
3053 *
3054 * Side effects:
3055 * Puts the proc structure to the close list so the reaper thread
3056 * can eventually close it. Assumes global lock is held.
3057 *
3058 *----------------------------------------------------------------------
3059 */
3060
3061static void
3062CloseWorker(Worker *workerPtr, const Ns_Time *timePtr)
3063{
3064 NS_NONNULL_ASSERT(workerPtr != NULL)((void) (0));
3065
3066 Ns_Log(Ns_LogNsProxyDebug, "nsproxy [%s]: close worker %ld (expire %ld ms)",
3067 workerPtr->poolPtr->name, (long) workerPtr->pid,
3068 timePtr != NULL((void*)0) ? (long)Ns_TimeToMilliseconds(timePtr) : -1);
3069
3070 /*
3071 * Set the time to kill the worker process. Reaper thread will use passed
3072 * time to wait for the worker process to exit gracefully. Otherwise, it
3073 * will start attempts to stop the worker by sending signals to it (polite
3074 * and unpolite).
3075 */
3076
3077 SetExpire(workerPtr, timePtr);
3078
3079 /*
3080 * Closing the write pipe should normally make proxy exit.
3081 */
3082
3083 ns_closeclose(workerPtr->wfd);
3084 workerPtr->signal = 0;
3085 workerPtr->sigsent = 0;
3086
3087 /*
3088 * Put on the head of the close list so it is handled by
3089 * the reaper thread.
3090 */
3091
3092 workerPtr->nextPtr = firstClosePtr;
3093 firstClosePtr = workerPtr;
3094
3095 Ns_Log(Ns_LogNsProxyDebug, "nsproxy [%s]: worker %ld closed",
3096 workerPtr->poolPtr->name, (long) workerPtr->pid);
3097}
3098
3099
3100static int
3101CloseWorkerOfProxy(Proxy *proxyPtr, const char *proxyId, const Ns_Time *timePtr)
3102{
3103 int reap = 0;
3104
3105 NS_NONNULL_ASSERT(proxyPtr != NULL)((void) (0));
3106
3107 if (proxyId == NULL((void*)0) || STREQ(proxyId, proxyPtr->id)(((*(proxyId)) == (*(proxyPtr->id))) && (strcmp((proxyId
),(proxyPtr->id)) == 0))
) {
3108 if (proxyPtr->workerPtr != NULL((void*)0)) {
3109 CloseWorker(proxyPtr->workerPtr, timePtr);
3110 proxyPtr->workerPtr = NULL((void*)0);
3111 reap = 1;
3112 }
3113 }
3114 return reap;
3115}
3116
3117
3118/*
3119 *----------------------------------------------------------------------
3120 *
3121 * CloseProxy --
3122 *
3123 * Close the given proxy handle.
3124 *
3125 * Results:
3126 * None.
3127 *
3128 * Side effects:
3129 * Starts the thread which reaps worker processes.
3130 *
3131 *----------------------------------------------------------------------
3132 */
3133
3134static void
3135CloseProxy(Proxy *proxyPtr)
3136{
3137 NS_NONNULL_ASSERT(proxyPtr)((void) (0));
3138
3139 if (proxyPtr->workerPtr != NULL((void*)0)) {
3140 Ns_MutexLock(&plock);
3141 CloseWorker(proxyPtr->workerPtr, &proxyPtr->conf.twait);
3142 proxyPtr->workerPtr = NULL((void*)0);
3143 proxyPtr->numruns = 0;
3144 Ns_MutexUnlock(&plock);
3145 ReapProxies();
3146 }
3147}
3148
3149
3150/*
3151 *----------------------------------------------------------------------
3152 *
3153 * ReaperThread --
3154 *
3155 * Detached thread which closes expired workers or workers
3156 * explicitly put on the close list.
3157 *
3158 * Results:
3159 * None.
3160 *
3161 * Side effects:
3162 * None.
3163 *
3164 *----------------------------------------------------------------------
3165 */
3166
3167static void
3168ReaperThread(void *UNUSED(arg)UNUSED_arg __attribute__((__unused__)))
3169{
3170 Tcl_HashSearch search;
3171 Proxy *proxyPtr, *prevPtr, *nextPtr;
3172 Pool *poolPtr;
3173 Worker *workerPtr, *tmpWorkerPtr;
3174 Ns_Time timeout, now, diff;
3175 long ntotal;
3176
3177 Ns_ThreadSetName("-nsproxy:reap-");
3178 Ns_Log(Notice, "starting");
3179
3180 Ns_MutexLock(&plock);
3181
3182 reaperState = Running;
3183 Ns_CondSignal(&pcond); /* Wakeup starter thread */
3184
3185 for (;;) {
3186 Tcl_HashEntry *hPtr;
3187 Worker *prevWorkerPtr;
3188
3189 Ns_GetTime(&now);
3190
3191 timeout.sec = TIME_T_MAX9223372036854775807L;
3192 timeout.usec = 0;
3193
3194 Ns_Log(Ns_LogNsProxyDebug, "reaper run");
3195
3196 /*
3197 * Check all proxy pools and see if there are
3198 * idle processes we can get rid off. Also
3199 * adjust the time to wait until the next
3200 * run of the loop.
3201 */
3202
3203 hPtr = Tcl_FirstHashEntry(&pools, &search);
3204 while (hPtr != NULL((void*)0)) {
3205
3206 /*
3207 * Get max time to wait for the whole pool
3208 */
3209
3210 poolPtr = (Pool *)Tcl_GetHashValue(hPtr)((hPtr)->clientData);
3211
3212 Ns_Log(Ns_LogNsProxyDebug, "reaper checks pool %s", poolPtr->name);
3213
3214 Ns_MutexLock(&poolPtr->lock);
3215 if (poolPtr->conf.tidle.sec != 0 && poolPtr->conf.tidle.usec != 0) {
3216 diff = now;
3217 Ns_IncrTime(&diff, poolPtr->conf.tidle.sec, poolPtr->conf.tidle.usec);
3218 if (Ns_DiffTime(&diff, &timeout, NULL((void*)0)) < 0) {
3219 timeout = diff;
3220 Ns_Log(Ns_LogNsProxyDebug, "reaper sets timeout based on idle "
3221 "diff " NS_TIME_FMT"%" "l" "d" ".%06ld" " of pool %s",
3222 (int64_t)timeout.sec, timeout.usec, poolPtr->name);
3223 }
3224 }
3225
3226 /*
3227 * Get max time to wait for one of the worker process.
3228 * This is less than the time for the whole pool.
3229 */
3230
3231 proxyPtr = poolPtr->firstPtr;
3232 prevPtr = NULL((void*)0);
3233 while (proxyPtr != NULL((void*)0)) {
3234 bool_Bool expired;
3235
3236 nextPtr = proxyPtr->nextPtr;
3237 workerPtr = proxyPtr->workerPtr;
3238 ntotal = poolPtr->nfree + poolPtr->nused;
3239 if (workerPtr != NULL((void*)0)) {
3240 expired = (Ns_DiffTime(&workerPtr->expire, &now, NULL((void*)0)) <= 0);
3241 Ns_Log(Ns_LogNsProxyDebug, "pool %s worker %ld expired %d",
3242 poolPtr->name, (long)workerPtr->pid, expired);
3243
3244 if (!expired && Ns_DiffTime(&workerPtr->expire, &timeout, NULL((void*)0)) <= 0) {
3245 timeout = workerPtr->expire;
3246 Ns_Log(Ns_LogNsProxyDebug, "reaper sets timeout based on "
3247 "expire " NS_TIME_FMT"%" "l" "d" ".%06ld" " pool %s worker %ld",
3248 (int64_t)timeout.sec, timeout.usec, poolPtr->name,
3249 (long)workerPtr->pid);
3250 }
3251 } else {
3252 expired = NS_FALSE0;
3253 }
3254 if (poolPtr->maxworker < ntotal) {
3255 /*
3256 * Prune the excessive proxy and close the worker.
3257 */
3258 if (prevPtr != NULL((void*)0)) {
3259 prevPtr->nextPtr = proxyPtr->nextPtr;
3260 }
3261 if (proxyPtr == poolPtr->firstPtr) {
3262 poolPtr->firstPtr = proxyPtr->nextPtr;
3263 }
3264 if (workerPtr != NULL((void*)0)) {
3265 CloseWorker(workerPtr, &proxyPtr->conf.twait);
3266 }
3267 FreeProxy(proxyPtr);
3268 proxyPtr = NULL((void*)0);
3269 poolPtr->nfree--;
3270 } else if (expired) {
3271 /*
3272 * Close the worker but leave the proxy.
3273 */
3274 CloseWorker(proxyPtr->workerPtr, &proxyPtr->conf.twait);
3275 proxyPtr->workerPtr = NULL((void*)0);
3276 }
3277 if (proxyPtr != NULL((void*)0)) {
3278 prevPtr = proxyPtr;
3279 }
3280 proxyPtr = nextPtr;
3281 }
3282 Ns_MutexUnlock(&poolPtr->lock);
3283 hPtr = Tcl_NextHashEntry(&search);
3284 }
3285
3286 /*
3287 * Check any closing procs. Also adjust the time
3288 * to wait until the next run of the loop.
3289 */
3290
3291 workerPtr = firstClosePtr;
3292 prevWorkerPtr = NULL((void*)0);
3293
3294 while (workerPtr != NULL((void*)0)) {
3295 if (Ns_DiffTime(&now, &workerPtr->expire, NULL((void*)0)) > 0) {
3296
3297 /*
3298 * Stop time expired, add new quantum and signal
3299 * the process to exit. After first quantum has
3300 * expired, be polite and try the TERM signal.
3301 * If this does not get the process down within
3302 * the second quantum, try the KILL signal.
3303 * If this does not get the process down within
3304 * the third quantum, abort - we have a zombie.
3305 */
3306
3307 Ns_IncrTime(&workerPtr->expire,
3308 workerPtr->poolPtr->conf.twait.sec,
3309 workerPtr->poolPtr->conf.twait.usec);
3310 switch (workerPtr->signal) {
3311 case 0: workerPtr->signal = SIGTERM15; break;
3312 case SIGTERM15: workerPtr->signal = SIGKILL9; break;
3313 case SIGKILL9: workerPtr->signal = -1; break;
3314 }
3315 }
3316
3317 if (workerPtr->signal == -1
3318 || workerPtr->rfd == NS_INVALID_FD(-1)
3319 || WaitFd(workerPtr->rfd, POLLIN0x001, 0)) {
3320
3321 /*
3322 * We either have timeouted eval (rfd==NS_INVALID_FD), a
3323 * zombie or the process has exited ok so splice it out the
3324 * list.
3325 */
3326
3327 if (prevWorkerPtr != NULL((void*)0)) {
3328 prevWorkerPtr->nextPtr = workerPtr->nextPtr;
3329 } else {
3330 firstClosePtr = workerPtr->nextPtr;
3331 }
3332
3333 if (workerPtr->signal == -1) {
3334 Ns_Log(Warning, "nsproxy: zombie: %ld", (long)workerPtr->pid);
3335 } else {
3336 int waitStatus = 0;
3337
3338 /*
3339 * Pass waitStatus ptr to Ns_WaitForProcessStatus() to
3340 * indicate that we want to handle the signal here and to
3341 * suppress warning entries in the error.log.
3342 *
3343 * The following wait operation should not really wait
3344 * but it is better to play safe.
3345 */
3346
3347 Ns_MutexUnlock(&plock);
3348 (void) Ns_WaitForProcessStatus(workerPtr->pid, NULL((void*)0), &waitStatus);
3349 Ns_MutexLock(&plock);
3350#ifdef WTERMSIG
3351 if (workerPtr->signal != 0 && WTERMSIG(waitStatus)((waitStatus) & 0x7f) != 0) {
3352 Ns_LogSeverity severity;
3353
3354 if (WTERMSIG(waitStatus)((waitStatus) & 0x7f) != workerPtr->signal) {
3355 severity = Warning;
3356 } else {
3357 severity = Notice;
3358 }
3359 Ns_Log(severity, "nsproxy process %d killed with signal %d (%s)",
3360 workerPtr->pid,
3361 WTERMSIG(waitStatus)((waitStatus) & 0x7f), strsignal(WTERMSIG(waitStatus)((waitStatus) & 0x7f)));
3362 }
3363#endif
3364 }
3365
3366 tmpWorkerPtr = workerPtr->nextPtr;
3367 if (workerPtr->rfd != NS_INVALID_FD(-1)) {
3368 ns_closeclose(workerPtr->rfd);
3369 }
3370 ns_free(workerPtr);
3371 workerPtr = tmpWorkerPtr;
3372
3373 } else {
3374
3375 /*
3376 * Process is still around, try killing it but leave it
3377 * in the list. Calculate the latest time we'll visit
3378 * this one again.
3379 */
3380
3381 if (Ns_DiffTime(&workerPtr->expire, &timeout, NULL((void*)0)) < 0) {
3382 Ns_Log(Ns_LogNsProxyDebug, "reaper shortens timeout to "
3383 NS_TIME_FMT"%" "l" "d" ".%06ld" " based on expire in pool %s worker %ld kill %d",
3384 (int64_t)timeout.sec, timeout.usec, workerPtr->poolPtr->name,
3385 (long)workerPtr->pid, workerPtr->signal);
3386 timeout = workerPtr->expire;
3387 }
3388 if (workerPtr->signal != workerPtr->sigsent) {
3389 Ns_Log(Warning, "[%s]: pid %ld won't die, send signal %d",
3390 workerPtr->poolPtr->name, (long)workerPtr->pid,
3391 workerPtr->signal);
3392 if (kill(workerPtr->pid, workerPtr->signal) != 0 && errno(*__errno_location ()) != ESRCH3) {
3393 Ns_Log(Error, "kill(%ld, %d) failed: %s",
3394 (long)workerPtr->pid, workerPtr->signal, strerror(errno(*__errno_location ())));
3395 }
3396 workerPtr->sigsent = workerPtr->signal;
3397 }
3398 prevWorkerPtr = workerPtr;
3399 workerPtr = workerPtr->nextPtr;
3400 }
3401 }
3402
3403 /*
3404 * Here we wait until signaled, or at most the
3405 * time we need to expire next worker or kill
3406 * some of them found on the close list.
3407 */
3408
3409 if (Ns_DiffTime(&timeout, &now, &diff) > 0) {
3410 reaperState = Sleeping;
3411 Ns_CondBroadcast(&pcond);
3412 if (timeout.sec == TIME_T_MAX9223372036854775807L && timeout.usec == 0) {
3413 Ns_Log(Ns_LogNsProxyDebug, "reaper waits unlimited for cond");
3414 Ns_CondWait(&pcond, &plock);
3415 } else {
3416 Ns_Log(Ns_LogNsProxyDebug, "reaper waits for cond with timeout " NS_TIME_FMT"%" "l" "d" ".%06ld",
3417 (int64_t)timeout.sec, timeout.usec);
3418 (void) Ns_CondTimedWait(&pcond, &plock, &timeout);
3419 }
3420 if (reaperState == Stopping) {
3421 break;
3422 }
3423 reaperState = Running;
3424 }
3425 }
3426
3427 reaperState = Stopped;
3428 Ns_CondSignal(&pcond);
3429 Ns_MutexUnlock(&plock);
3430
3431 Ns_Log(Notice, "exiting");
3432}
3433
3434
3435/*
3436 *----------------------------------------------------------------------
3437 *
3438 * FreeProxy --
3439 *
3440 * Disposes a proxy handle.
3441 *
3442 * Results:
3443 * None.
3444 *
3445 * Side effects:
3446 * None.
3447 *
3448 *----------------------------------------------------------------------
3449 */
3450
3451static void
3452FreeProxy(Proxy *proxyPtr)
3453{
3454 NS_NONNULL_ASSERT(proxyPtr)((void) (0));
3455
3456 Tcl_DStringFree(&proxyPtr->in);
3457 Tcl_DStringFree(&proxyPtr->out);
3458 ns_free(proxyPtr->id);
3459 ns_free(proxyPtr);
3460}
3461
3462
3463/*
3464 *----------------------------------------------------------------------
3465 *
3466 * FreePool --
3467 *
3468 * Disposes a pool handle (call only on server exit).
3469 *
3470 * Results:
3471 * None.
3472 *
3473 * Side effects:
3474 * None.
3475 *
3476 *----------------------------------------------------------------------
3477 */
3478
3479static void
3480FreePool(Pool *poolPtr)
3481{
3482 NS_NONNULL_ASSERT(poolPtr != NULL)((void) (0));
3483
3484 if (poolPtr->exec != NULL((void*)0)) {
3485 ns_free((char *)poolPtr->exec);
3486 }
3487 if (poolPtr->init != NULL((void*)0)) {
3488 ns_free((char *)poolPtr->init);
3489 }
3490 if (poolPtr->reinit != NULL((void*)0)) {
3491 ns_free((char *)poolPtr->reinit);
3492 }
3493 if (poolPtr->env) {
3494 Ns_SetFree(poolPtr->env);
3495 }
3496
3497 Ns_CondDestroy(&poolPtr->cond);
3498 Ns_MutexDestroy(&poolPtr->lock);
3499
3500 ns_free((char *)poolPtr);
3501}
3502
3503
3504/*
3505 *----------------------------------------------------------------------
3506 *
3507 * PushProxy --
3508 *
3509 * Return a proxy to the pool.
3510 *
3511 * Results:
3512 * None.
3513 *
3514 * Side effects:
3515 * None.
3516 *
3517 *----------------------------------------------------------------------
3518 */
3519
3520static void
3521PushProxy(Proxy *proxyPtr)
3522{
3523 Pool *poolPtr;
3524
3525 NS_NONNULL_ASSERT(proxyPtr != NULL)((void) (0));
3526
3527 poolPtr = proxyPtr->poolPtr;
3528 /*
3529 * Clears the proxy for the next use
3530 */
3531
3532 ResetProxy(proxyPtr);
3533
3534 /*
3535 * Divorce from the per-interpreter tables
3536 */
3537
3538 if (proxyPtr->cntPtr != NULL((void*)0)) {
3539 intptr_t nhave = (intptr_t) Tcl_GetHashValue(proxyPtr->cntPtr)((proxyPtr->cntPtr)->clientData);
3540
3541 nhave--;
3542 Tcl_SetHashValue(proxyPtr->cntPtr, (ClientData) nhave)((proxyPtr->cntPtr)->clientData = (ClientData) ((ClientData
) nhave))
;
3543 if (proxyPtr->idPtr != NULL((void*)0)) {
3544 Tcl_DeleteHashEntry(proxyPtr->idPtr);
3545 proxyPtr->idPtr = NULL((void*)0);
3546 }
3547 proxyPtr->cntPtr = NULL((void*)0);
3548 }
3549
3550 /*
3551 * Return the proxy to the pool, pruning it if
3552 * its addition to the pool will break limits.
3553 */
3554
3555 Ns_MutexLock(&poolPtr->lock);
3556 poolPtr->nused--;
3557 if ((poolPtr->nused + poolPtr->nfree) <= poolPtr->maxworker) {
3558 proxyPtr->nextPtr = poolPtr->firstPtr;
3559 poolPtr->firstPtr = proxyPtr;
3560 if (proxyPtr->workerPtr != NULL((void*)0)) {
3561 SetExpire(proxyPtr->workerPtr, &proxyPtr->conf.tidle);
3562 }
3563 proxyPtr->conf = poolPtr->conf;
3564 proxyPtr = NULL((void*)0);
3565 poolPtr->nfree++;
3566 Ns_CondBroadcast(&poolPtr->cond);
3567 }
3568 Ns_MutexUnlock(&poolPtr->lock);
3569
3570 /*
3571 * Check for an excessive proxy
3572 */
3573
3574 if (proxyPtr != NULL((void*)0)) {
3575 CloseProxy(proxyPtr);
3576 FreeProxy(proxyPtr);
3577 }
3578}
3579
3580
3581/*
3582 *----------------------------------------------------------------------
3583 *
3584 * ReleaseProxy --
3585 *
3586 * Release a proxy from the per-interp table.
3587 *
3588 * Results:
3589 * Result of reinit call or TCL_OK if no reinit.
3590 *
3591 * Side effects:
3592 * None.
3593 *
3594 *----------------------------------------------------------------------
3595 */
3596
3597static int
3598ReleaseProxy(Tcl_Interp *interp, Proxy *proxyPtr)
3599{
3600 int result = TCL_OK0;
3601 Tcl_CmdInfo cmdinfo;
3602
3603 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
3604 NS_NONNULL_ASSERT(proxyPtr != NULL)((void) (0));
3605
3606 if (proxyPtr->state == Idle) {
3607 Tcl_DString ds;
3608 int reinit;
3609
3610 Tcl_DStringInit(&ds);
3611 Ns_MutexLock(&proxyPtr->poolPtr->lock);
3612 reinit = proxyPtr->poolPtr->reinit != NULL((void*)0);
3613 if (reinit != 0) {
3614 Tcl_DStringAppend(&ds, proxyPtr->poolPtr->reinit, -1);
3615 }
3616 Ns_MutexUnlock(&proxyPtr->poolPtr->lock);
3617 if (reinit != 0) {
3618 result = Eval(interp, proxyPtr, Tcl_DStringValue(&ds)((&ds)->string), NULL((void*)0));
3619 }
3620 Tcl_DStringFree(&ds);
3621
3622 } else if ( (proxyPtr->state == Busy) && (proxyPtr->workerPtr != NULL((void*)0)) ) {
3623 proxyPtr->workerPtr->signal = 0;
3624 Ns_Log(Notice, "releasing busy proxy %s", proxyPtr->id);
3625
3626 /*
3627 * In case the proxy is busy, make sure to drain the pipe, otherwise
3628 * the proxy might be hanging in a send operation. Closing our end
3629 * causes in the worker process an exception and terminates the
3630 * potentially blocking write operation.
3631 */
3632 ns_closeclose(proxyPtr->workerPtr->rfd);
3633 proxyPtr->workerPtr->rfd = NS_INVALID_FD(-1);
3634 }
3635 if (proxyPtr->cmdToken != NULL((void*)0)) {
3636 /*
3637 * Modify command definition inline so it does not
3638 * attempt to call us recursively when deleted.
3639 */
3640 Tcl_GetCommandInfoFromToken(proxyPtr->cmdToken, &cmdinfo);
3641 cmdinfo.deleteProc = NULL((void*)0);
3642 cmdinfo.deleteData = NULL((void*)0);
3643 Tcl_SetCommandInfoFromToken(proxyPtr->cmdToken, &cmdinfo);
3644 Tcl_DeleteCommand(interp, proxyPtr->id);
3645 }
3646
3647 PushProxy(proxyPtr);
3648
3649 return result;
3650}
3651
3652
3653/*
3654 *----------------------------------------------------------------------
3655 *
3656 * RunProxyObjCmd --
3657 *
3658 * Activated when somebody calls proxy command.
3659 *
3660 * Results:
3661 * Result of the script as with Tcl eval.
3662 *
3663 * Side effects:
3664 * None.
3665 *
3666 *----------------------------------------------------------------------
3667 */
3668
3669static int
3670RunProxyObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
3671{
3672 char *scriptString;
3673 int result;
3674 Ns_Time *timeoutPtr = NULL((void*)0);
3675 Ns_ObjvSpec args[] = {
3676 {"script", Ns_ObjvString, &scriptString, NULL((void*)0)},
3677 {"?timeout", Ns_ObjvTime, &timeoutPtr, NULL((void*)0)},
3678 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
3679 };
3680
3681 if (Ns_ParseObjv(NULL((void*)0), args, interp, 1, objc, objv) != NS_OK) {
3682 result = TCL_ERROR1;
3683
3684 } else {
3685 Proxy *proxyPtr = (Proxy *)clientData;
3686
3687 result = Eval(interp, proxyPtr, scriptString, timeoutPtr);
3688 }
3689 return result;
3690}
3691
3692
3693/*
3694 *----------------------------------------------------------------------
3695 *
3696 * DelProxyProc --
3697 *
3698 * Release a proxy from the per-interp table.
3699 *
3700 * Results:
3701 * Result of reinit call or TCL_OK if no reinit.
3702 *
3703 * Side effects:
3704 * None.
3705 *
3706 *----------------------------------------------------------------------
3707 */
3708
3709static void
3710DelProxyProc(ClientData clientData)
3711{
3712 Proxy *proxyPtr = (Proxy *)clientData;
3713
3714 /*
3715 * Prevents the ReleaseProxy to attempt to delete the associated
3716 * Tcl command, which would call us recursively otherwise.
3717 */
3718
3719 proxyPtr->cmdToken = NULL((void*)0);
3720 ReleaseProxy(proxyPtr->interp, proxyPtr);
3721}
3722
3723
3724/*
3725 *----------------------------------------------------------------------
3726 *
3727 * ReleaseHandles --
3728 *
3729 * Release any remaining handles in the per-interp table.
3730 *
3731 * Results:
3732 * None.
3733 *
3734 * Side effects:
3735 * None.
3736 *
3737 *----------------------------------------------------------------------
3738 */
3739
3740static void
3741ReleaseHandles(Tcl_Interp *interp, InterpData *idataPtr)
3742{
3743 Tcl_HashEntry *hPtr;
3744 Tcl_HashSearch search;
3745
3746 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
3747 NS_NONNULL_ASSERT(idataPtr != NULL)((void) (0));
3748
3749 hPtr = Tcl_FirstHashEntry(&idataPtr->ids, &search);
3750 while (hPtr != NULL((void*)0)) {
3751 Proxy *proxyPtr = (Proxy *)Tcl_GetHashValue(hPtr)((hPtr)->clientData);
3752
3753 (void) ReleaseProxy(interp, proxyPtr);
3754 hPtr = Tcl_NextHashEntry(&search);
3755 }
3756}
3757
3758
3759/*
3760 *----------------------------------------------------------------------
3761 *
3762 * DeleteData --
3763 *
3764 * Tcl assoc data cleanup for ns_proxy.
3765 *
3766 * Results:
3767 * None.
3768 *
3769 * Side effects:
3770 * None.
3771 *
3772 *----------------------------------------------------------------------
3773 */
3774
3775static void
3776DeleteData(ClientData clientData, Tcl_Interp *interp)
3777{
3778 InterpData *idataPtr = clientData;
3779
3780 ReleaseHandles(interp, idataPtr);
3781 Tcl_DeleteHashTable(&idataPtr->ids);
3782 Tcl_DeleteHashTable(&idataPtr->cnts);
3783 ns_free(idataPtr);
3784}
3785
3786
3787/*
3788 *----------------------------------------------------------------------
3789 *
3790 * ReapProxies --
3791 *
3792 * Wakes up the reaper thread and waits until it does
3793 * its job and goes sleeping again.
3794 *
3795 * Results:
3796 * None.
3797 *
3798 * Side effects:
3799 * Will start the reaper thread if not already running.
3800 *
3801 *----------------------------------------------------------------------
3802 */
3803
3804static void
3805ReapProxies(void)
3806{
3807 Ns_MutexLock(&plock);
3808 if (reaperState == Stopped) {
3809 reaperState = Starting;
3810 Ns_ThreadCreate(ReaperThread, NULL((void*)0), 0, NULL((void*)0));
3811 } else {
3812 reaperState = Awaken;
3813 Ns_CondSignal(&pcond);
3814 }
3815 while (reaperState != Sleeping) {
3816 Ns_CondWait(&pcond, &plock);
3817 }
3818 Ns_MutexUnlock(&plock);
3819}
3820
3821
3822/*
3823 *----------------------------------------------------------------------
3824 *
3825 * GetTimeDiff --
3826 *
3827 * Returns time difference in milliseconds between current time
3828 * and time given in passed structure. If the current time is
3829 * later than the passed time, the result is negative.
3830 *
3831 * Results:
3832 * Number of milliseconds (may be negative!)
3833 *
3834 * Side effects:
3835 * None.
3836 *
3837 *----------------------------------------------------------------------
3838 */
3839
3840static long
3841GetTimeDiff(const Ns_Time *timePtr)
3842{
3843 Ns_Time now, diff;
3844
3845 NS_NONNULL_ASSERT(timePtr != NULL)((void) (0));
3846
3847 Ns_GetTime(&now);
3848 Ns_DiffTime(timePtr, &now, &diff);
3849 return (long)Ns_TimeToMilliseconds(&diff);
3850}
3851
3852/*
3853 *----------------------------------------------------------------------
3854 *
3855 * ProxyError --
3856 *
3857 * Formats an extended error message.
3858 *
3859 * Results:
3860 * None.
3861 *
3862 * Side effects:
3863 * Will set the errorCode global variable.
3864 *
3865 *----------------------------------------------------------------------
3866 */
3867
3868static void
3869ProxyError(Tcl_Interp *interp, Err err)
3870{
3871 const char *sysmsg = NULL((void*)0);
3872
3873 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
3874
3875 Tcl_SetErrorCode(interp, "NSPROXY", errCode[err], errMsg[err], sysmsg, (char *)0L);
3876}
3877
3878/*
3879 * Local Variables:
3880 * mode: c
3881 * c-basic-offset: 4
3882 * fill-column: 78
3883 * indent-tabs-mode: nil
3884 * End:
3885 */