diff --git a/src/python/build_pyrte3.com b/src/python/build_pyrte3.com index 9f21a62c1055b5b818c32c9850f57633a0ec207d_c3JjL3B5dGhvbi9idWlsZF9weXJ0ZTMuY29t..aff157d9025310d1eee56d29d81fd9d7ee58a922_c3JjL3B5dGhvbi9idWlsZF9weXJ0ZTMuY29t 100644 --- a/src/python/build_pyrte3.com +++ b/src/python/build_pyrte3.com @@ -64,7 +64,7 @@ $! $ SET NOON $ SET VERIFY -$ LINK /THREADS_ENABLE/EXECUTABLE=WASD_EXE:PYRTE.EXE - +$ LINK /THREADS_ENABLE/EXECUTABLE=WASD_EXE:PYRTE3.EXE - [.OBJ_'ARCH_NAME']PYRTE.OBJ, - SYS$INPUT:/OPTIONS IDENTIFICATION="PYRTE 3.0.0" diff --git a/src/python/pyrte.c b/src/python/pyrte.c index 9f21a62c1055b5b818c32c9850f57633a0ec207d_c3JjL3B5dGhvbi9weXJ0ZS5j..aff157d9025310d1eee56d29d81fd9d7ee58a922_c3JjL3B5dGhvbi9weXJ0ZS5j 100644 --- a/src/python/pyrte.c +++ b/src/python/pyrte.c @@ -27,9 +27,9 @@ import wasd while wasd.cgiplus_begin(): - print 'Content-type: text/html' - print '' - print '<h1><i>hello world!</i></h1>' + print ('Content-type: text/html') + print ('') + print ('<h1><i>hello world!</i></h1>') The cgiplus_begin() method accepts zero, one or two positional parameters. @@ -50,5 +50,44 @@ activated as a CGIplus script. +DEBUGGING SCRIPTS +----------------- +Always fun :-/ PyRTE v2.0/3.0 provides some execution data via the WATCH +[x]Script report. For example (note the SCRIPT items): + +|00:30:50.08 SERVICE 1735 093001 CONNECT VIRTUAL klaatu.local:443| +|00:30:50.08 REQUEST 4318 093001 REQUEST GET /py-bin/wsgi_test3.py| +|00:30:50.09 CACHE 0604 093001 RESPONSE CACHE search path 5F1A99EFCB98C60126B8F244110E5BDD| +|00:30:50.09 DCL 1572 093001 RESPONSE SCRIPT as HTTP$NOBODY RTE /py-bin/wsgi_test3.py wasd_root:[src.python.scripts]wsgi_test3.py ($cgi_exe:pyrte.exe)| +|00:30:50.10 DCL 7962 125001 SCRIPT PYRTE AXP-2.0.0 Python 2.7.18 (default, Jul 23 2020, 17:40:05) [DECC]| +|00:30:50.10 DCL 7945 093001 SCRIPT RTE caching /py-bin/wsgi_test3.py /WASD_ROOT/src/PYTHON/SCRIPTS/wsgi_test3.py| +|00:30:50.10 DCL 7945 093001 SCRIPT CACHE new 7/7| +|00:30:50.10 DCL 7945 093001 SCRIPT LOAD /py-bin/wsgi_test3.py /WASD_ROOT/src/PYTHON/SCRIPTS/wsgi_test3.py| +|00:30:50.10 DCL 7945 093001 SCRIPT CODE /WASD_ROOT/src/PYTHON/SCRIPTS/wsgi_test3.py| +|00:30:50.11 DCL 7945 093001 SCRIPT EVAL /py-bin/wsgi_test3.py| +|00:30:50.12 DCL 7945 093001 SCRIPT PYRTE AXP-2.0.0 USAGE:1/19 REAL:00:00:00.02 CPU:0.02 DIO:3 BIO:36 FAULTS:0 PGFL:463040/8%| +|00:30:50.12 GZIP 0608 093001 RESPONSE DEFLATE 1289->507 bytes, 39% (261kB)| +|00:30:50.12 REQUEST 1438 093001 REQUEST STATUS 200 (OK) rx:78 tx:2415 bytes 1.045832 seconds 2,383 B/s| + +Script errors are flagged in the report. + +|00:33:50.17 DCL 7962 265001 SCRIPT EVAL /py-bin/pyrte_test3.py| +|00:33:56.82 DCL 7962 265001 SCRIPT ERROR <type 'exceptions.RuntimeError'> 'logical name PYRTE_CACHE_ENTRY not defined'| + +It is also possible to add items from within the script Python code using the +wasd.WATCH() function. The example /py-bin/pyrte_test1.py script contains the +statement + + wasd.WATCH('and hello [x]Script') + +|00:35:15.05 DCL 7945 057001 SCRIPT RTE caching /py-bin/pyrte_test1.py /WASD_ROOT/src/PYTHON/SCRIPTS/pyrte_test1.py| +|00:35:15.05 DCL 7945 057001 SCRIPT CACHE new 1/1| +|00:35:15.05 DCL 7945 057001 SCRIPT LOAD /py-bin/pyrte_test1.py /WASD_ROOT/src/PYTHON/SCRIPTS/pyrte_test1.py| +|00:35:15.05 DCL 7945 057001 SCRIPT CODE /WASD_ROOT/src/PYTHON/SCRIPTS/pyrte_test1.py| +|00:35:15.06 DCL 7945 057001 SCRIPT EVAL /py-bin/pyrte_test1.py| +|00:35:15.07 DCL 7945 057001 SCRIPT WATCH and hello [x]Script| +|00:35:15.07 DCL 7945 057001 SCRIPT PYRTE AXP-2.0.0 USAGE:1/1 REAL:00:00:05.10 CPU:5.06 DIO:65 BIO:1060 FAULTS:870 PGFL:476448/5%| + + LATENCIES --------- @@ -53,16 +92,9 @@ LATENCIES --------- -Establishing a Python interpreter is quite expensive in terms of CPU cycles and -latency. When pyRTE creates a new interpreter for each request it means that -script name-spaces are completely isolated. On my test-bench PWS500 this adds -approximately 250mS to each request. pyRTE attempts to ameliorate the effects -of this overhead by creating the interpreter prior to accepting the next -request. For back-to-back requests this gains nothing but where there is at -least 250mS between requests (minimum, and for example) reduces response time -latency by that amount. Hence the simple "hello world" test example for -back-to-back requests (generated using ApacheBench or WASD-bench) each is -reported by HTTPDMON as having a duration of approximately 280mS, while -one-at-a-time using a browser each is reported at between 10 and 15 mS. +pyRTE version prior to 2.0/3.0 used subinterpreters to process independent +requests. Post 2.0/3.0 pyRTE uses the one interpreter and threads. With the +suggested and default caching RTE these threads are cleared and remain with +loaded code between requests. BY DEFAULT, pyRTE takes AN AGGRESSIVE POSITION ON REDUCING LATENCY. This leaves the interpreter intact for each unique script. The standard CGI "hello @@ -122,6 +154,7 @@ Read a record from the CGIPLUSIN stream (caution!) + wasd.reuse_thread() wasd.reuse_interpreter() See discussion of 'latencies' above. Takes one arugment; True to reuse @@ -148,6 +181,7 @@ Returns an integer representing the number of times the cached byte-code for this particular script has been (re)used. + wasd.usage_thread() wasd.usage_interpreter() Returns an integer representing the number of times this (sub)interpreter @@ -157,6 +191,10 @@ Returns an integer representing the number of times this RTE has been used. + wasd.watch() + + Writes a message to WATCH [x]Script (useful for debugging). + wasd.write() Writes directly to <stdout> with a following fflush(). @@ -195,8 +233,8 @@ REFERENCES ---------- -Python/C API Reference Manual, Release 2.4.3 -Extending and Embedding the Python Interpreter, Release 2.4.3 +Python/C API Reference Manual, Release 2.7.18 and 3.8 +Extending and Embedding the Python Interpreter, Release 2.7.18 and 3.8 Lots of Googling with many and varied query strings. http://python3porting.com/cextensions.html @@ -280,8 +318,8 @@ therefore per-script) basis using mapping rules. script=param=PYRTE=/HEAD=GET same as logical PYRTE_HEAD_CVT_GET - script=param=PYRTE=/REUSE reuse this Python interpreter - script=param=PYRTE=/NOREUSE do not reuse this Python interpreter + script=param=PYRTE=/REUSE reuse this Python thread + script=param=PYRTE=/NOREUSE do not reuse this Python thread script=param=PYRTE=/NOSTREAM do not issue the stream-mode header script=param=PYRTE=/WSGI=BUFFER force WSGI buffering mode @@ -292,9 +330,10 @@ if some unique part of the script debugs just that script if defined to something like 'c' or '/' or '-' debugs all (this script-name check is CASE-SENSITIVE) +PYRTE_ARGV multi-valued logical name containing Python CLI switches PYRTE_BASIC_RTE full interpreter cycle request processing (for comparison) PYRTE_HEAD_CVT_GET if a HEAD method convert to a GET method as per Apache mod_wsgi behaviour PYRTE_CACHE_ENTRY enables function RtePyMethRteCache() PYRTE_CACHE_MAX integer size of code cache (zero to compare uncached) PYRTE_METRICS enable metric collection and report via "?$metrics$" query @@ -295,8 +334,9 @@ PYRTE_BASIC_RTE full interpreter cycle request processing (for comparison) PYRTE_HEAD_CVT_GET if a HEAD method convert to a GET method as per Apache mod_wsgi behaviour PYRTE_CACHE_ENTRY enables function RtePyMethRteCache() PYRTE_CACHE_MAX integer size of code cache (zero to compare uncached) PYRTE_METRICS enable metric collection and report via "?$metrics$" query -PYRTE_NOREUSE_INTERPRETER do not reuse interpreter for each script instance +PYRTE_NOREUSE_THREAD do not reuse thread for each script instance +PYRTE_NOREUSE_INTERPRETER backward compatibility with PYRTE_NOREUSE_THREAD PYRTE_ONESHOT voluntary exit after a single use (see PYRTE_USAGE_LIMIT) @@ -302,5 +342,4 @@ PYRTE_ONESHOT voluntary exit after a single use (see PYRTE_USAGE_LIMIT) -PYRTE_STAT_TIMER incorporate basic stats in HTML comment PYRTE_USAGE_LIMIT integer number of requests before voluntary exit PYRTE_WSGI_FORCE_BUFFERING enable buffering with WASD, this breaks the WSGI specification but may give a performance boost. @@ -314,7 +353,7 @@ VERSION HISTORY (update SOFTWAREVN as well!) --------------- -20-AUG-2020 MGD v3.0.0, Python 3 (via JFP Python 3.10.0a0) +08-SEP-2020 MGD v3.0.0, Python 3 (via JFP Python 3.10.0a0) v2.0.0, Python 2 equivalent Python 2.7.18 build kludge for VSI C V7.4-001 on OpenVMS IA64 V8.4-2L1 @@ -318,6 +357,8 @@ v2.0.0, Python 2 equivalent Python 2.7.18 build kludge for VSI C V7.4-001 on OpenVMS IA64 V8.4-2L1 + subinterpreters are out and threads in! + add WATCH [x]Script watch-points and wasd.WATCH() 22-JUL-2017 MGD v1.1.12, wasd.cgi_callout and RtePyMethCgiCallout() provides callout environment regardless of WSGI bugfix; stderr = freopen(stderr); @@ -349,7 +390,7 @@ /*****************************************************************************/ #define SOFTWARECR "Copyright (C) 2007-2020 Mark G.Daniel" -#define SOFTWAREVN "x.0.0" /* just the minor and tweak versions here! */ +#define SOFTWAREVN "x.0.0" /** 'x' will be updated with '2' or '3' below **/ /** ----------------^^^^^ and IDENTIFICATION=x.n.n in BUILD_PYRTE[n].COM **/ #define SOFTWARENM "PYRTE" #ifdef __ALPHA @@ -441,6 +482,11 @@ /* macros */ /**********/ +/* mainly to allow easy use of the __unaligned directive */ +#define ULONGPTR __unaligned unsigned long* +#define USHORTPTR __unaligned unsigned short* +#define INT64PTR __unaligned __int64* + /** INTERIM **/ #if PY_MAJOR_VERSION >= 3 #define PyArg_ParseTuple _PyArg_ParseTuple_SizeT @@ -469,12 +515,7 @@ #define FI_LI __FILE__, __LINE__ -/* mainly to allow easy use of the __unaligned directive */ -#define ULONGPTR __unaligned unsigned long* -#define USHORTPTR __unaligned unsigned short* -#define INT64PTR __unaligned __int64* - /******************/ /* global storage */ /******************/ @@ -477,6 +518,6 @@ /******************/ /* global storage */ /******************/ -int dbug, +int dbug, watches, AppOutputCount, @@ -482,3 +523,3 @@ AppOutputCount, - CgiPlusPythonEnd, + CgiPlusState, CodeMetricEnabled, @@ -484,5 +525,5 @@ CodeMetricEnabled, - GlobalDebug, + CurrentTimeSecs, HeadCvtGet, IsCgiPlusMode, IsCgiPlusScript, @@ -486,4 +527,6 @@ HeadCvtGet, IsCgiPlusMode, IsCgiPlusScript, + IsScriptRte, + MainArgc, NoStreamMode, @@ -489,3 +532,4 @@ NoStreamMode, - ReuseInterpreter, + PyrteUsageCount, + ReuseThread, RunStarted, @@ -491,5 +535,4 @@ RunStarted, - UsageCount, UsageLimit, WasdHeadersSent, WsgiHeadersSent, @@ -511,6 +554,8 @@ *CgiPlusEotPtr, *CgiPlusEscPtr; +char **MainArgv; + char CgiPlusEof [64], CgiPlusEot [64], CgiPlusEsc [64]; @@ -532,8 +577,9 @@ *pOsDict, *pOsEnvironDict, *pOsModule, + *pStdout, *pWasdModule, *pWsgiExcInfo, *pWsgiHeaders, *pWsgiStatus; @@ -535,8 +581,10 @@ *pWasdModule, *pWsgiExcInfo, *pWsgiHeaders, *pWsgiStatus; +PyInterpreterState *pInterpState; + struct AnExitHandler { unsigned long Flink; @@ -579,7 +627,7 @@ struct CodeCacheStruct { int CodeUsageCount, - InterpreterUsageCount; + ThreadUsageCount; unsigned long LastUsedSecs, ScriptMtimeSecs; char *FileNamePtr, @@ -583,8 +631,10 @@ unsigned long LastUsedSecs, ScriptMtimeSecs; char *FileNamePtr, - *FileNameByteCodePtr; - PyThreadState *pInterpState; + *FileNameByteCodePtr, + *ScriptNamePtr; + PyThreadState *pThreadState; + PyObject *pMainDict2; #if PY_MAJOR_VERSION >= 3 PyObject *pByteCode; #else @@ -635,9 +685,10 @@ PyDoc_STRVAR (rte_read_cgiplusin__doc__, "Retuns a string containing a record read from the CGIPLUSIN stream."); -static PyObject* RtePyMethReuseInterpreter (); - -PyDoc_STRVAR (rte_reuse_interpreter__doc__, +static PyObject* RtePyMethReuseThread (); +static PyObject* RtePyMethReuseInterpreter (); /* backward compatibility */ + +PyDoc_STRVAR (rte_reuse_thread__doc__, "Enables (True) or disables (False) interpreter reuse for the next request \ received by pyRTE."); @@ -666,5 +717,6 @@ "Returns an integer value for how many times the cached byte-code \ has been used."); +static PyObject* RtePyMethUsageThread (); static PyObject* RtePyMethUsageInterpreter (); @@ -669,4 +721,4 @@ static PyObject* RtePyMethUsageInterpreter (); -PyDoc_STRVAR (rte_usage_interpreter__doc__, +PyDoc_STRVAR (rte_usage_thread__doc__, "Returns an integer value for how many times the current Python \ @@ -672,8 +724,8 @@ "Returns an integer value for how many times the current Python \ -(sub)interpreter has been used."); +thread has been used."); static PyObject* RtePyMethUsageRte (); PyDoc_STRVAR (rte_usage_rte__doc__, "Returns an integer value for how many times the RTE has been used."); @@ -674,9 +726,14 @@ static PyObject* RtePyMethUsageRte (); PyDoc_STRVAR (rte_usage_rte__doc__, "Returns an integer value for how many times the RTE has been used."); +static PyObject* RteWATCH (PyObject*, PyObject*); + +PyDoc_STRVAR (wsgi_watch__doc__, +"Write to WATCH [x]Script (useful for debugging)."); + /* WSGI methods */ static PyObject* WsgiRun (PyObject*, PyObject*); @@ -715,8 +772,11 @@ rte_getvar__doc__ }, { "read_cgiplusin", (PyCFunction)RtePyMethReadCgiPlusIn, METH_VARARGS, rte_read_cgiplusin__doc__ }, - { "reuse_interpreter", (PyCFunction)RtePyMethReuseInterpreter, METH_VARARGS, - rte_reuse_interpreter__doc__ }, + { "reuse_thread", (PyCFunction)RtePyMethReuseThread, METH_VARARGS, + rte_reuse_thread__doc__ }, + /* and backward compatibility */ + { "reuse_interpreter", (PyCFunction)RtePyMethReuseThread, METH_VARARGS, + rte_reuse_thread__doc__ }, { "rte_cache_entry", (PyCFunction)RtePyMethRteCacheEntry, METH_VARARGS, rte_cache_entry__doc__ }, { "rte_id", (PyCFunction)RtePyMethRteId, METH_VARARGS, @@ -725,7 +785,10 @@ rte_stat_timer__doc__ }, { "usage_code", (PyCFunction)RtePyMethUsageCode, METH_NOARGS, rte_usage_code__doc__ }, - { "usage_interpreter", (PyCFunction)RtePyMethUsageInterpreter, METH_NOARGS, - rte_usage_interpreter__doc__ }, + { "usage_thread", (PyCFunction)RtePyMethUsageThread, METH_NOARGS, + rte_usage_thread__doc__ }, + /* and backward compatibility */ + { "usage_interpreter", (PyCFunction)RtePyMethUsageThread, METH_NOARGS, + rte_usage_thread__doc__ }, { "usage_rte", (PyCFunction)RtePyMethUsageRte, METH_NOARGS, rte_usage_rte__doc__ }, @@ -730,3 +793,7 @@ { "usage_rte", (PyCFunction)RtePyMethUsageRte, METH_NOARGS, rte_usage_rte__doc__ }, + { "WATCH", (PyCFunction)RteWATCH, METH_VARARGS, + wsgi_watch__doc__ }, + { "watch", (PyCFunction)RteWATCH, METH_VARARGS, + wsgi_watch__doc__ }, { "write", (PyCFunction)RteStdoutWrite, METH_VARARGS, @@ -732,5 +799,5 @@ { "write", (PyCFunction)RteStdoutWrite, METH_VARARGS, - wsgi_write__doc__ }, + wsgi_write__doc__ }, /* WSGI methods */ @@ -776,6 +843,8 @@ void at_Exit (); void AddMetrics (struct CodeCacheStruct*); int ByteCodeUpToDate (struct CodeCacheStruct*); +void CgiPlusBegin (); +void CgiPlusEnd (); char* CgiVar (char*); char* CgiVarDclSymbol (char*); void CollectMetrics (struct CodeCacheStruct*, int); @@ -779,6 +848,6 @@ char* CgiVar (char*); char* CgiVarDclSymbol (char*); void CollectMetrics (struct CodeCacheStruct*, int); -void DbugCheck (); -int EmptyScriptName (char*, char*); +int DbugCheck (int); +char* EmptyScriptName (char*); void EnsureExit (unsigned long*); @@ -784,2 +853,3 @@ void EnsureExit (unsigned long*); +void EvalByteCode (struct CodeCacheStruct*); void FlushCacheEntry (struct CodeCacheStruct*); @@ -785,6 +855,7 @@ void FlushCacheEntry (struct CodeCacheStruct*); +PyThreadState* FreshThread (PyThreadState*, char*); int lib$stop (__unknown_params); int LibVmReport (void*); void LoadByteCode (char*, char*, struct CodeCacheStruct*); int MetricsReport (); int ModuleCgiibLoaded (); @@ -786,10 +857,11 @@ int lib$stop (__unknown_params); int LibVmReport (void*); void LoadByteCode (char*, char*, struct CodeCacheStruct*); int MetricsReport (); int ModuleCgiibLoaded (); -void ProcessCLI (char*); -void ProcessCGI (int, char**); -void ProcessCGIplus (int, char**); +int ProcessArgcArgv (); +void ProcessCLI (); +void ProcessCGI (); +void ProcessCGIplus (); void ProcessBasicRte (); void ProcessCachingRte (); @@ -794,4 +866,5 @@ void ProcessBasicRte (); void ProcessCachingRte (); +void ProcessPyRun (); int ProctorDetect (); int ReadFileIntoMemory (char*, char**, int*); @@ -796,4 +869,4 @@ int ProctorDetect (); int ReadFileIntoMemory (char*, char**, int*); -void ReportError (int, int, int, char*, ...); +int ReportError (int, int, int, char*, ...); void RteScriptParam (); @@ -799,4 +872,6 @@ void RteScriptParam (); +int ServerSoftware (void); +void SetArgv (char*); void SetProgramName (char*); int SetupOsEnvCgiVar (int); char* StatTimer (int); @@ -800,5 +875,7 @@ void SetProgramName (char*); int SetupOsEnvCgiVar (int); char* StatTimer (int); -char* TrnLnm (char*, char*); +void StatTimerCallout (int); +char* TrnLnm (char*); +char* Trn2Lnm (char*, char*, int); int WasdModuleInit (void); @@ -804,4 +881,6 @@ int WasdModuleInit (void); +void WatchCallout (char*, ...); +void WatchErrorCallout (); int WsgiSendHeaders (); int WsgiSendResponse (PyObject*); int WsgiOsEnvVar (); @@ -805,11 +884,6 @@ int WsgiSendHeaders (); int WsgiSendResponse (PyObject*); int WsgiOsEnvVar (); -#if PY_MAJOR_VERSION >= 3 -wchar_t** MungeArgv (int, char*[]); -#else -char** MungeArgv (int, char*[]); -#endif #if PY_MAJOR_VERSION >= 3 PyAPI_FUNC(void) PyNode_Free (struct _mod*); @@ -852,8 +926,11 @@ *strchr(SoftwareId,'x') = '2'; #endif + MainArgc = argc; + MainArgv = argv; + /* set the global pointer used by some functions */ CodeCachePtr = &CodeCache[0]; if (argc == 2) { @@ -855,7 +932,14 @@ /* set the global pointer used by some functions */ CodeCachePtr = &CodeCache[0]; if (argc == 2) { + if (!strncasecmp(argv[1], "/run=", 5)) + { + DbugCheck (1); + argv[1] += 5; + ProcessPyRun (); + exit (SS$_NORMAL); + } if (!strncasecmp(argv[1], "/cli=", 5)) { @@ -860,8 +944,9 @@ if (!strncasecmp(argv[1], "/cli=", 5)) { - DbugCheck(); - ProcessCLI (argv[1]+5); + DbugCheck (1); + argv[1] += 5; + ProcessCLI (); exit (SS$_NORMAL); } if (!strcasecmp(argv[1], "/version")) { @@ -864,10 +949,11 @@ exit (SS$_NORMAL); } if (!strcasecmp(argv[1], "/version")) { - fprintf (stdout, "%%PYRTE-I-VERSION, %s (%s)\n%s\n%s", - SoftwareId, BUILD_DATETIME, SOFTWARECR, SOFTWAREGPL); + fprintf (stdout, "%%PYRTE-I-VERSION, %s (%s) Python %s\n%s\n%s", + SoftwareId, BUILD_DATETIME, Py_GetVersion(), + SOFTWARECR, SOFTWAREGPL); exit (SS$_NORMAL); } } @@ -870,7 +956,15 @@ exit (SS$_NORMAL); } } +#if PY_MAJOR_VERSION >= 3 + if (argc >= 2) + { + ProcessArgcArgv (); + exit (SS$_NORMAL); + } +#endif + status = sys$getjpiw (0, 0, 0, &JpiItems, 0, 0, 0); if (!(status & 1)) exit (status); @@ -874,9 +968,4 @@ status = sys$getjpiw (0, 0, 0, &JpiItems, 0, 0, 0); if (!(status & 1)) exit (status); - DbugCheck(); - - WsgiForceBuffering = - (TrnLnm ("PYRTE_WSGI_FORCE_BUFFERING", NULL) != NULL); - /* if it doesn't look like CGI environment then forget it */ @@ -882,5 +971,16 @@ /* if it doesn't look like CGI environment then forget it */ - if (!TrnLnm ("HTTP$INPUT", NULL)) exit (SS$_ABORT); - - if (!dbug) + if (!TrnLnm ("HTTP$INPUT")) exit (SS$_ABORT); + + WsgiForceBuffering = (TrnLnm ("PYRTE_WSGI_FORCE_BUFFERING") != NULL); + + cptr = TrnLnm ("PYRTE_USAGE_LIMIT"); + if (cptr && isdigit(*cptr)) UsageLimit = atoi(cptr); + + if (TrnLnm ("PYRTE_ONESHOT")) UsageLimit = 1; + + /* binary mode to eliminate carriage-control */ + if (!(stdin = freopen ("HTTP$INPUT:", "r", stdin, "ctx=bin"))) + exit (vaxc$errno); + + if (!DbugCheck(1)) { @@ -886,8 +986,4 @@ { - /* binary mode to eliminate carriage-control */ - if (!(stdin = freopen ("HTTP$INPUT:", "r", stdin, - "ctx=bin", "ctx=xplct"))) - exit (vaxc$errno); if (!(stdout = freopen ("SYS$OUTPUT:", "w", stdout, "ctx=bin", "ctx=xplct"))) exit (vaxc$errno); @@ -891,7 +987,7 @@ if (!(stdout = freopen ("SYS$OUTPUT:", "w", stdout, "ctx=bin", "ctx=xplct"))) exit (vaxc$errno); - if (!(stderr = freopen ("SYS$ERROR:", "w", stderr, - "ctx=bin", "ctx=xplct"))) - exit (vaxc$errno); + /* PY_MAJOR_VERSION >= 3 required interleaving output and errors */ + fclose (stderr); + stderr = stdout; } @@ -897,8 +993,4 @@ } - - cptr = TrnLnm ("PYRTE_USAGE_LIMIT", NULL); - if (cptr && isdigit(*cptr)) UsageLimit = atoi(cptr); - - if (TrnLnm ("PYRTE_ONESHOT", NULL)) UsageLimit = 1; + dbug = 0; /* need differentiate between RTE/CGIplus and CGIplus script */ @@ -903,12 +995,14 @@ /* need differentiate between RTE/CGIplus and CGIplus script */ - IsCgiPlusMode = (TrnLnm ("CGIPLUSEOF", NULL) != NULL); - CgiPlusEofPtr = TrnLnm ("CGIPLUSEOF", CgiPlusEof); - CgiPlusEotPtr = TrnLnm ("CGIPLUSEOT", CgiPlusEot); - CgiPlusEscPtr = TrnLnm ("CGIPLUSESC", CgiPlusEsc); + CgiPlusEofPtr = strdup(TrnLnm ("CGIPLUSEOF")); + CgiPlusEotPtr = strdup(TrnLnm ("CGIPLUSEOT")); + CgiPlusEscPtr = strdup(TrnLnm ("CGIPLUSESC")); + IsCgiPlusMode = (CgiPlusEofPtr != NULL); + + WasdModuleInit (); /* set up and declare the exit handler */ ExitHandler.HandlerAddress = (unsigned long)&EnsureExit; ExitHandler.ArgCount = 1; ExitHandler.ExitStatusPtr = (unsigned long)&ExitStatus; status = sys$dclexh (&ExitHandler); @@ -909,10 +1003,59 @@ /* set up and declare the exit handler */ ExitHandler.HandlerAddress = (unsigned long)&EnsureExit; ExitHandler.ArgCount = 1; ExitHandler.ExitStatusPtr = (unsigned long)&ExitStatus; status = sys$dclexh (&ExitHandler); - if (!(status & 0x00000001)) exit (status); + if (!(status & 1)) exit (status); + + Py_Initialize(); + PyEval_InitThreads (); +#if PY_MAJOR_VERSION >= 3 + pInterpState = PyInterpreterState_Main (); +#else + pInterpState = PyInterpreterState_Head (); +#endif + + /* get handle to python stdout file ready for explicit flushing */ + pStdout = PySys_GetObject ("stdout"); + if (!pStdout) + { + ReportError (__LINE__, 0, 1, ErrorPython); + exit (SS$_NORMAL); + } + + /* borrowed reference */ + pMainModule = PyImport_AddModule ("__main__"); + if (!pMainModule) + { + ReportError (__LINE__, 0, 1, ErrorPython); + return; + } + + /* borrowed reference */ + pMainDict = PyModule_GetDict (pMainModule); + if (!pMainDict) + { + ReportError (__LINE__, 0, 1, ErrorPython); + return; + } + DEBUGPYOBJ("pMainDict",pMainDict) + + /* borrowed reference */ + pOsModule = PyImport_AddModule ("os"); + if (!pOsModule) + { + ReportError (__LINE__, 0, 1, ErrorPython); + return; + } + /* borrowed reference */ + pOsDict = PyModule_GetDict (pOsModule); + if (!pOsDict) + { + ReportError (__LINE__, 0, 1, ErrorPython); + return; + } + DEBUGPYOBJ("pOsDict",pOsDict) if (IsCgiPlusMode) { @@ -916,10 +1059,16 @@ if (IsCgiPlusMode) { - /* for pyRTE an RTE will only have one parameter, CGIplus two! */ - if (argc == 1) + /* get the first request (which is immediately available) */ + CgiVar (""); + + if (CgiVar("WATCH_SCRIPT") != NULL) + WatchCallout ("%s Python %s", SoftwareId, Py_GetVersion()); + + /* only an RTE will have this variable */ + if (IsScriptRte = (CgiVar ("SCRIPT_RTE") != NULL)) { /*******/ /* RTE */ /*******/ @@ -921,9 +1070,9 @@ { /*******/ /* RTE */ /*******/ - if (TrnLnm ("PYRTE_BASIC_RTE", NULL)) + if (TrnLnm ("PYRTE_BASIC_RTE")) ProcessBasicRte (); else ProcessCachingRte (); @@ -935,7 +1084,7 @@ /***********/ /* supply the command-line script file name */ - ProcessCGIplus (argc, argv); + ProcessCGIplus (); } } else @@ -944,6 +1093,6 @@ /* CGI */ /*******/ - ProcessCGI (argc, argv); + ProcessCGI (); } @@ -948,5 +1097,7 @@ } + Py_Finalize (); + exit (SS$_NORMAL); } @@ -973,7 +1124,47 @@ /*****************************************************************************/ /* +Process using standard Python arguments at the command-line. +For base-line comparison purposes only. +*/ + +#if PY_MAJOR_VERSION >= 3 + +int ProcessArgcArgv () + +{ + PyConfig config; + PyStatus status; + + /*********/ + /* begin */ + /*********/ + + if (DbugCheck(1)) fprintf (stdout, "ProcessArgcArgv()\n"); + + PyConfig_InitPythonConfig (&config); + config.isolated = 1; + + status = PyConfig_SetBytesArgv (&config, MainArgc, MainArgv); + if (PyStatus_Exception(status)) goto fail; + + status = Py_InitializeFromConfig (&config); + if (PyStatus_Exception (status)) goto fail; + PyConfig_Clear (&config); + + return (Py_RunMain ()); + +fail: + PyConfig_Clear (&config); + if (PyStatus_IsExit(status)) return (status.exitcode); + Py_ExitStatusException(status); +} + +#endif + +/*****************************************************************************/ +/* Process from the command-line. For base-line comparison purposes only. */ @@ -976,7 +1167,7 @@ Process from the command-line. For base-line comparison purposes only. */ -void ProcessCLI (char *ScriptName) +void ProcessPyRun () { @@ -981,6 +1172,6 @@ { - int status; + int argc, status; char *cptr, *sptr; FILE *fp; @@ -988,7 +1179,6 @@ /* begin */ /*********/ - if (dbug) fprintf (stdout, "ProcessCLI()\n"); - - SetProgramName (ScriptName); + if (DbugCheck(1)) fprintf (stdout, "ProcessPyRun()\n"); + WasdModuleInit (); @@ -994,7 +1184,8 @@ WasdModuleInit (); - Py_Initialize(); - - fp = fopen (ScriptName, "r"); + Py_Initialize (); + SetArgv (NULL); + + fp = fopen (MainArgv[1], "r"); if (fp) { if (dbug) fprintf (stdout, "fp:%u\n", fp); @@ -998,9 +1189,9 @@ if (fp) { if (dbug) fprintf (stdout, "fp:%u\n", fp); - PyRun_AnyFileExFlags (fp, ScriptName, 0, NULL); + PyRun_AnyFileExFlags (fp, MainArgv[1], 0, NULL); fclose (fp); } else { status = vaxc$errno; @@ -1002,10 +1193,10 @@ fclose (fp); } else { status = vaxc$errno; - ReportError (__LINE__, status, 0, "Error opening !AZ", ScriptName); + ReportError (__LINE__, status, 0, "Error opening !AZ", MainArgv[1]); } Py_Finalize (); @@ -1008,9 +1199,50 @@ } Py_Finalize (); - if (dbug || TrnLnm ("PYRTE_STAT_TIMER",NULL)) - fprintf (stdout, "<!-- %s -->\n", StatTimer(TRUE)); + if (dbug || TrnLnm ("PYRTE_STAT_TIMER")) + fprintf (stdout, "%s\n", StatTimer(TRUE)); +} + +/*****************************************************************************/ +/* +Process from the command-line. +For base-line comparison purposes only. +*/ + +void ProcessCLI () + +{ + int argc; + struct CodeCacheStruct *ccptr; + + /*********/ + /* begin */ + /*********/ + + if (DbugCheck(1)) fprintf (stdout, "ProcessCLI()\n"); + + WasdModuleInit (); + Py_Initialize(); + PyEval_InitThreads (); + SetArgv (MainArgv[1]); + +#if PY_MAJOR_VERSION >= 3 + pInterpState = PyInterpreterState_Main (); +#else + pInterpState = PyInterpreterState_Head (); +#endif + + ccptr = &CodeCache[0]; + ccptr->FileNamePtr = ccptr->ScriptNamePtr = MainArgv[1]; + ccptr->pThreadState = PyThreadState_New (pInterpState); + LoadByteCode (MainArgv[1], MainArgv[1], ccptr); + if (ccptr->pByteCode) EvalByteCode (ccptr); + + Py_Finalize (); + + if (dbug || TrnLnm ("PYRTE_STAT_TIMER")) + fprintf (stdout, "%s\n", StatTimer(TRUE)); } /*****************************************************************************/ @@ -1019,9 +1251,6 @@ For base-line comparison purposes only. */ -void ProcessCGI -( -int argc, -char **argv -) +void ProcessCGI () + { @@ -1027,9 +1256,9 @@ { - int status; - char *cptr, *sptr; - FILE *fp; + char *fnptr, *snptr; + struct CodeCacheStruct *ccptr; + PyThreadState *pStateBuffer; /*********/ /* begin */ /*********/ @@ -1031,16 +1260,19 @@ /*********/ /* begin */ /*********/ - if (dbug) fprintf (stdout, "ProcessCGI()\n"); - - cptr = sptr = CgiVar ("PATH_INFO"); - fp = fopen (cptr, "r"); - if (!fp) - { - sptr = CgiVar ("PATH_TRANSLATED"); - fp = fopen (sptr, "r"); - } - if (fp) + if (DbugCheck(1)) fprintf (stdout, "ProcessCGI()\n"); + + watches = (CgiVar("WATCH_SCRIPT") != NULL); + + fnptr = CgiVar ("PATH_TRANSLATED"); + snptr = CgiVar ("PATH_INFO"); + + if (watches) WatchCallout ("CGI %s %s", snptr, fnptr); + + StatTimer(FALSE); + + LoadByteCode (fnptr, snptr, (ccptr = &CodeCache[0])); + if (ccptr->pByteCode) { @@ -1046,18 +1278,8 @@ { - StatTimer(FALSE); - - SetProgramName (cptr); - WasdModuleInit (); - Py_Initialize(); - - PySys_SetArgv (argc, MungeArgv(argc,argv)); - - PyRun_AnyFileExFlags (fp, cptr, 0, NULL); - - if (dbug || TrnLnm ("PYRTE_STAT_TIMER",NULL)) - fprintf (stdout, "<!-- %s -->\n", StatTimer(TRUE)); - - Py_Finalize(); - - fclose (fp); + ccptr->pThreadState = FreshThread (NULL, snptr); + pStateBuffer = PyThreadState_Swap (ccptr->pThreadState); + EvalByteCode (ccptr); + if (ReportError (0, 0, 0, NULL)) FlushCacheEntry (ccptr); + PyThreadState_Swap (pStateBuffer); + FreshThread (ccptr->pThreadState, NULL); } @@ -1063,9 +1285,6 @@ } - else - { - status = vaxc$errno; - ReportError (__LINE__, status, 0, "Error opening !AZ", sptr); - } + + if (watches) StatTimerCallout (1); } /*****************************************************************************/ @@ -1073,9 +1292,6 @@ For a Python script that implements CGIplus itself. */ -void ProcessCGIplus -( -int argc, -char **argv -) +void ProcessCGIplus () + { @@ -1081,4 +1297,3 @@ { - int status; - char *cptr, *sptr; + char *fnptr, *snptr; struct CodeCacheStruct *ccptr; @@ -1084,7 +1299,7 @@ struct CodeCacheStruct *ccptr; - PyObject *pResult; + PyThreadState *pStateBuffer; /*********/ /* begin */ /*********/ @@ -1086,9 +1301,11 @@ /*********/ /* begin */ /*********/ - if (dbug) fprintf (stdout, "ProcessCGIplus() %d\n", argc); + if (DbugCheck(1)) fprintf (stdout, "ProcessCgiPlus()\n"); + + watches = (CgiVar("WATCH_SCRIPT") != NULL); /* code behaviours are different for an explicitly CGIplus script */ IsCgiPlusScript = 1; @@ -1092,42 +1309,15 @@ /* code behaviours are different for an explicitly CGIplus script */ IsCgiPlusScript = 1; - - SetProgramName (argv[1]); - Py_Initialize(); - - /* just use the first element of the code cache */ - ccptr = &CodeCache[0]; - - /* it will have been used the once! */ - ccptr->InterpreterUsageCount++; - - /* first is RTE image, second CGIplus script, then optional parameters */ - PySys_SetArgv (argc-1, MungeArgv(argc,&argv[1])); - - LoadByteCode (argv[1], argv[1], ccptr); - if (!ccptr->pByteCode) return; - - pMainModule = PyImport_AddModule ("__main__"); - if (!pMainModule) - { - ReportError (__LINE__, 0, 1, ErrorPython); - return; - } - pMainDict = PyModule_GetDict (pMainModule); - if (!pMainDict) - { - ReportError (__LINE__, 0, 1, ErrorPython); - return; - } - DEBUGPYOBJ("pMainDict",pMainDict) - - pOsModule = PyImport_AddModule ("os"); - if (!pOsModule) - { - ReportError (__LINE__, 0, 1, ErrorPython); - return; - } - pOsDict = PyModule_GetDict (pOsModule); - if (!pOsDict) + PyrteUsageCount++; + + StatTimer(FALSE); + + fnptr = CgiVar ("SCRIPT_FILENAME"); + snptr = CgiVar ("SCRIPT_NAME"); + + if (watches) WatchCallout ("CGIplus %s %s", snptr, fnptr); + + LoadByteCode (fnptr, snptr, (ccptr = &CodeCache[0])); + if (ccptr->pByteCode) { @@ -1133,4 +1323,8 @@ { - ReportError (__LINE__, 0, 1, ErrorPython); - return; + ccptr->pThreadState = FreshThread (NULL, snptr); + pStateBuffer = PyThreadState_Swap (ccptr->pThreadState); + EvalByteCode (ccptr); + if (ReportError (0, 0, 0, NULL)) FlushCacheEntry (ccptr); + PyThreadState_Swap (pStateBuffer); + FreshThread (ccptr->pThreadState, NULL); } @@ -1136,40 +1330,6 @@ } - DEBUGPYOBJ("pOsDict",pOsDict) - - if (!WasdModuleInit ()) return; - - /* get the first request (which is already available and won't block) */ - CgiVar (""); - - DbugCheck(); - - RteScriptParam (); - - if (!NoStreamMode) fprintf (stdout, "Script-Control: X-stream-mode\n"); - - if (!SetupOsEnvCgiVar (1)) return; - - PyErr_Clear (); - - pResult = PyEval_EvalCode (ccptr->pByteCode, pMainDict, pMainDict); - if (pError = PyErr_Occurred()) - { - ReportError (__LINE__, 0, 1, NULL); - FlushCacheEntry (ccptr); - } - DEBUGPYOBJ("PyEval_EvalCode",pResult) - Py_XDECREF (pResult); - - fflush (stdout); - fputs (CgiPlusEofPtr, stdout); - fflush (stdout); - - if (!GlobalDebug) dbug = 0; - - Py_Finalize(); - - if (dbug || TrnLnm ("PYRTE_STAT_TIMER",NULL)) - fprintf (stdout, "<!-- %s -->\n", StatTimer(TRUE)); + + DbugCheck (0); } /*****************************************************************************/ @@ -1182,10 +1342,9 @@ void ProcessBasicRte () { - int status; - char *cptr, *sptr; - char *argv [2] = { NULL, NULL }; - FILE *fp; + char *fnptr, *snptr; + struct CodeCacheStruct *ccptr; + PyThreadState *pStateBuffer; /*********/ /* begin */ @@ -1195,32 +1354,18 @@ for (;;) { - AppOutputCount = 0; - - /* initialise this for special CGI-and-CGIplus script cases */ - RtePyMethCgiPlusBegin (NULL, NULL); - - SetProgramName ("ProcessBasicRte()"); - if (!WasdModuleInit()) break; - Py_Initialize(); - - /* block waiting for the first/next request */ - CgiVar (""); - UsageCount++; - - DbugCheck(); - - cptr = sptr = CgiVar ("SCRIPT_FILENAME"); - SetProgramName (cptr); - - /* fudge these */ - argv[0] = cptr; - PySys_SetArgv (1, MungeArgv(1,argv)); - - RteScriptParam (); - - if (!NoStreamMode) fprintf (stdout, "Script-Control: X-stream-mode\n"); - - fp = fopen (sptr, "r", "shr=get"); - if (fp) + CgiPlusBegin (); + + if (dbug) fprintf (stdout, "ProcessBasicRte()\n"); + + watches = (CgiVar("WATCH_SCRIPT") != NULL); + + fnptr = CgiVar ("SCRIPT_FILENAME"); + snptr = CgiVar ("SCRIPT_NAME"); + if (watches) WatchCallout ("RTE basic %s %s", snptr, fnptr); + + SetProgramName (snptr); + + LoadByteCode (fnptr, snptr, (ccptr = &CodeCache[0])); + if (ccptr->pByteCode) { @@ -1226,11 +1371,11 @@ { - PyRun_AnyFileExFlags (fp, sptr, 0, NULL); - fclose (fp); - } - else - if (!ProctorDetect ()) - { - status = vaxc$errno; - ReportError (__LINE__, status, 0, "Error opening !AZ", sptr); + ccptr->pThreadState = FreshThread (NULL, snptr); + pStateBuffer = PyThreadState_Swap (ccptr->pThreadState); + EvalByteCode (ccptr); + if (ReportError (0, 0, 0, NULL)) FlushCacheEntry (ccptr); + RtePyMethCgiPlusEnd (NULL, NULL); + PyThreadState_Swap (pStateBuffer); + /* always destroy the just-used thread */ + ccptr->pThreadState = FreshThread (ccptr->pThreadState, NULL); } @@ -1235,17 +1380,10 @@ } - Py_Finalize(); - - if (dbug || TrnLnm ("PYRTE_STAT_TIMER",NULL)) - fprintf (stdout, "<!-- %s -->\n", StatTimer(TRUE)); - - fflush (stdout); - fputs (CgiPlusEofPtr, stdout); - fflush (stdout); - - if (!GlobalDebug) dbug = 0; - - if (UsageLimit && UsageCount >= UsageLimit) break; + if (watches) StatTimerCallout (1); + + CgiPlusEnd (); + + if (UsageLimit && PyrteUsageCount >= UsageLimit) break; } } @@ -1262,6 +1400,5 @@ { int idx, tidx, retval, status; - unsigned long mtime, - CurrentTimeSecs; + unsigned long mtime; char *cptr, *fnptr, *sptr, *snptr; @@ -1267,5 +1404,4 @@ char *cptr, *fnptr, *sptr, *snptr; - char *argv [2] = { NULL, NULL }; struct CodeCacheStruct *ccptr; PyObject *pObject, *pResult; @@ -1276,5 +1412,5 @@ if (dbug) fprintf (stdout, "ProcessCachingRte()\n"); - cptr = TrnLnm ("PYRTE_CACHE_MAX", NULL); + cptr = TrnLnm ("PYRTE_CACHE_MAX"); if (cptr && isdigit(*cptr)) CodeCacheMax = atoi(cptr); @@ -1280,5 +1416,5 @@ if (cptr && isdigit(*cptr)) CodeCacheMax = atoi(cptr); - if (CodeCacheMax > CACHE_MAX) CodeCacheMax = CACHE_MAX; + if (CodeCacheMax <= 0 || CodeCacheMax > CACHE_MAX) CodeCacheMax = CACHE_MAX; for (;;) { @@ -1282,7 +1418,10 @@ for (;;) { + /* reduce latency by always keeping a fresh thread on hand */ + FreshThread (NULL, NULL); + /********/ /* wait */ /********/ @@ -1285,23 +1424,15 @@ /********/ /* wait */ /********/ - /* initialise this for special CGI-and-CGIplus script cases */ - RtePyMethCgiPlusBegin (NULL, NULL); - - SetProgramName ("ProcessBasicRte()"); - if (!WasdModuleInit()) break; - Py_Initialize(); - - /* block waiting for the first/next request */ - CgiVar (""); - - sys$gettim (&CurrentBinTime); - CurrentTimeSecs = decc$fix_time (&CurrentBinTime); - DbugCheck(); - - if (TrnLnm ("PYRTE_METRICS", NULL)) + CgiPlusBegin (); + + if (DbugCheck(1)) fprintf (stdout, "ProcessCachingRte()\n"); + + watches = (CgiVar("WATCH_SCRIPT") != NULL); + + if (TrnLnm ("PYRTE_METRICS")) CodeMetricEnabled = 1; else CodeMetricEnabled = 0; @@ -1304,7 +1435,8 @@ CodeMetricEnabled = 1; else CodeMetricEnabled = 0; - if (TrnLnm ("PYRTE_NOREUSE_INTERPRETER", NULL)) - ReuseInterpreter = 0; + if (TrnLnm ("PYRTE_NOREUSE_THREAD") || + TrnLnm ("PYRTE_NOREUSE_INTERPRETER")) + ReuseThread = 0; else @@ -1310,7 +1442,5 @@ else - ReuseInterpreter = 1; - - AppOutputCount = 0; + ReuseThread = 1; RteScriptParam (); @@ -1321,14 +1451,11 @@ /***********/ fnptr = CgiVar ("SCRIPT_FILENAME"); - if (!fnptr) - { - ReportError (__LINE__, 0, 0, "Error getting SCRIPT_FILENAME."); - break; - } + snptr = CgiVar ("SCRIPT_NAME"); + if (watches) WatchCallout ("RTE caching %s %s", snptr, fnptr); sptr = CgiVar ("QUERY_STRING"); if (*sptr == '$' && !strncmp (sptr, "$metrics$", 9) && MetricsReport()) continue; @@ -1329,12 +1456,9 @@ sptr = CgiVar ("QUERY_STRING"); if (*sptr == '$' && !strncmp (sptr, "$metrics$", 9) && MetricsReport()) continue; - /* delay to here so that metrics report is excluded */ - UsageCount++; - /*******************/ /* byte-code cache */ /*******************/ @@ -1347,7 +1471,7 @@ ccptr = &CodeCache[idx]; if (!ccptr->FileNamePtr) continue; if (dbug) fprintf (stdout, "%d %d |%s|%s|\n", - idx, ccptr->InterpreterUsageCount, + idx, ccptr->ThreadUsageCount, cptr, ccptr->FileNamePtr); if (!strcasecmp (fnptr, ccptr->FileNamePtr)) break; ccptr = NULL; @@ -1359,12 +1483,8 @@ /* found cache */ /***************/ - if (dbug) fprintf (stdout, "hit:%d\n", idx); - if (!ByteCodeUpToDate (ccptr)) - { - /* apparently not */ - FlushCacheEntry (ccptr); - } + if (watches) WatchCallout ("CACHE hit %d/%d", idx+1, CodeCacheCount); + if (!ByteCodeUpToDate (ccptr)) FlushCacheEntry (ccptr); } else if (CodeCacheMax) @@ -1377,9 +1497,13 @@ for (tidx = 0; tidx < CodeCacheCount; tidx++) if (!CodeCache[tidx].FileNamePtr) break; if (tidx < CodeCacheCount) - ccptr = &CodeCache[tidx]; + { + ccptr = &CodeCache[idx = tidx]; + if (watches) WatchCallout ("CACHE unused %d/%d", + idx+1, CodeCacheCount); + } else if (tidx < CodeCacheMax) { /* didn't find an unused entry */ CodeCacheCount++; @@ -1381,9 +1505,11 @@ else if (tidx < CodeCacheMax) { /* didn't find an unused entry */ CodeCacheCount++; - ccptr = &CodeCache[tidx]; + ccptr = &CodeCache[idx = tidx]; + if (watches) WatchCallout ("CACHE new %d/%d", + idx+1, CodeCacheCount); } else { @@ -1391,5 +1517,5 @@ for (idx = tidx = 0; idx < CodeCacheMax; idx++) if (CodeCache[idx].LastUsedSecs < CodeCache[tidx].LastUsedSecs) tidx = idx; - ccptr = &CodeCache[tidx]; + ccptr = &CodeCache[idx]; FlushCacheEntry (ccptr); @@ -1395,2 +1521,4 @@ FlushCacheEntry (ccptr); + if (watches) WatchCallout ("CACHE lru %d/%d", + idx+1, CodeCacheCount); } @@ -1396,6 +1524,5 @@ } - idx = tidx; } else { /* code caching is disabled */ @@ -1398,8 +1525,9 @@ } else { /* code caching is disabled */ - ccptr = &CodeCache[0]; + if (watches) WatchCallout ("CACHE disabled"); + ccptr = &CodeCache[idx = 0]; if (ccptr->FileNamePtr) FlushCacheEntry (ccptr); } if (dbug) fprintf (stdout, "CodeCacheCount:%d idx:%d usage:%d\n", @@ -1403,7 +1531,7 @@ if (ccptr->FileNamePtr) FlushCacheEntry (ccptr); } if (dbug) fprintf (stdout, "CodeCacheCount:%d idx:%d usage:%d\n", - CodeCacheCount, idx, ccptr->CodeUsageCount); + CodeCacheCount, idx+1, ccptr->CodeUsageCount); /* set the global pointer used by some functions */ CodeCachePtr = ccptr; @@ -1414,13 +1542,6 @@ /* load script */ /***************/ - snptr = CgiVar ("SCRIPT_NAME"); - if (!snptr) - { - ReportError (__LINE__, 0, 0, "Error getting SCRIPT_NAME."); - break; - } - LoadByteCode (fnptr, snptr, ccptr); AddMetrics (ccptr); @@ -1428,76 +1549,20 @@ CollectMetrics (ccptr, 0); - if (ccptr->pByteCode) - { - /*********************/ - /* execute byte-code */ - /*********************/ - - ccptr->CodeUsageCount++; - ccptr->LastUsedSecs = CurrentTimeSecs; - - /* borrowed reference */ - pMainModule = PyImport_AddModule ("__main__"); - if (!pMainModule) - { - ReportError (__LINE__, 0, 1, ErrorPython); - return; - } - /* borrowed reference */ - pMainDict = PyModule_GetDict (pMainModule); - if (!pMainDict) - { - ReportError (__LINE__, 0, 1, ErrorPython); - return; - } - DEBUGPYOBJ("pMainDict",pMainDict) - - /* borrowed reference */ - pOsModule = PyImport_AddModule ("os"); - if (!pOsModule) - { - ReportError (__LINE__, 0, 1, ErrorPython); - return; - } - /* borrowed reference */ - pOsDict = PyModule_GetDict (pOsModule); - if (!pOsDict) - { - ReportError (__LINE__, 0, 1, ErrorPython); - return; - } - DEBUGPYOBJ("pOsDict",pOsDict) - - if (!SetupOsEnvCgiVar (1)) return; - - SetProgramName (snptr); - - PyErr_Clear (); - - pResult = PyEval_EvalCode (ccptr->pByteCode, pMainDict, pMainDict); - /* new reference */ - if (pError = PyErr_Occurred()) - { - ReportError (__LINE__, 0, 1, NULL); - FlushCacheEntry (ccptr); - } - DEBUGPYOBJ("PyEval_EvalCode",pResult) - - if (!SetupOsEnvCgiVar (0)) return; - - /* new reference, can be NULL */ - Py_XDECREF (pResult); - } - else - { - /* no code - no cache! */ - FlushCacheEntry (ccptr); - } - - Py_Finalize(); + /******************/ + /* execute script */ + /******************/ + + EvalByteCode (ccptr); + + if (watches) StatTimerCallout (ccptr->ThreadUsageCount); + + /* after the flush to accumulate any outstanding BIO */ + CollectMetrics (ccptr, 1); + + if (ReportError (0, 0, 0, NULL)) FlushCacheEntry (ccptr); /******************/ /* end of request */ /******************/ @@ -1499,23 +1564,13 @@ /******************/ /* end of request */ /******************/ - /* after the flush to accumulate any outstanding BIO */ - CollectMetrics (ccptr, 1); - - if (dbug || TrnLnm ("PYRTE_STAT_TIMER",NULL)) - fprintf (stdout, "<!-- %s -->\n", StatTimer(TRUE)); - - fflush (stdout); - fputs (CgiPlusEofPtr, stdout); - fflush (stdout); - - if (!GlobalDebug) dbug = 0; - - if (UsageLimit && UsageCount >= UsageLimit) break; + CgiPlusEnd (); + + if (UsageLimit && PyrteUsageCount >= UsageLimit) break; } } /*****************************************************************************/ /* @@ -1517,9 +1572,382 @@ } } /*****************************************************************************/ /* -Free dynamic components and NULL related pointers. +If both parameters are NULL then create a new thread in advance. +If |pStaleState| is non-NULL remove that thread. +If |ScriptName| is non-NULL use the fresh thread and return it. +*/ + +PyThreadState* FreshThread +( +PyThreadState *pStaleState, +char *ScriptName +) +{ + static PyThreadState *pFreshState = NULL; + + int argc; + PyThreadState *pStateBuffer; + + /*********/ + /* begin */ + /*********/ + + if (dbug) fprintf (stdout, "FreshThread() %u |%s|\n", + (unsigned int)pStaleState, ScriptName); + + if (pStaleState) + { + /* finished with this thread */ + PyThreadState_Clear (pStaleState); + PyThreadState_Delete (pStaleState); + } + if (!pFreshState) + { + /* proactively create a thread (even if it's then used immediately) */ + pFreshState = PyThreadState_New (pInterpState); + if (!pFreshState) + { + ReportError (__LINE__, 0, 0, "Error initialising thread."); + exit (SS$_ABORT); + } + } + if (ScriptName) + { + /* return the fresh thread */ + pStateBuffer = PyThreadState_Swap (pFreshState); + SetProgramName (ScriptName); + SetArgv (ScriptName); + PyThreadState_Swap (pStateBuffer); + pStateBuffer = pFreshState; + pFreshState = NULL; + return (pStateBuffer); + } + return (NULL); +} + +/*****************************************************************************/ +/* +Execute the Python script (the loaded bytecode). +*/ + +void EvalByteCode (struct CodeCacheStruct *ccptr) + +{ + int error = 0; + char *argv [2] = { NULL, NULL }; + PyObject *pObject, + *pResult; + PyThreadState *pStateBuffer; + + /*********/ + /* begin */ + /*********/ + + if (dbug) fprintf (stdout, "EvalByteCode()\n"); + + if (watches) WatchCallout ("EVAL %s", ccptr->ScriptNamePtr); + + if (!ccptr->pByteCode) + { + if (ccptr->pThreadState) + { + /* destroy the current thread */ + ccptr->pThreadState = FreshThread (ccptr->pThreadState, NULL); + ccptr->ThreadUsageCount = 0; + } + FlushCacheEntry (ccptr); + return; + } + + ccptr->CodeUsageCount++; + ccptr->LastUsedSecs = CurrentTimeSecs; + +#if PY_MAJOR_VERSION >= 3 + if (dbug) fprintf (stdout, "PyGILState_Check() %d\n", PyGILState_Check()); +#endif + + if (!ccptr->pThreadState) + { + /* allocate a fresh thread to this entry */ + ccptr->pThreadState = FreshThread (NULL, ccptr->ScriptNamePtr); + ccptr->ThreadUsageCount = 0; + } + + /* use the associated thread */ + pStateBuffer = PyThreadState_Swap (ccptr->pThreadState); + ccptr->ThreadUsageCount++; + + /* borrowed reference */ +#if PY_MAJOR_VERSION >= 3 + pWasdModule = PyModule_Create (&RtePyModuleDef); +#else + pWasdModule = Py_InitModule ("wasd", RtePyMethods); +#endif + if (!pWasdModule) + { + ReportError (__LINE__, 0, 1, ErrorPython); + return; + } + + if (!SetupOsEnvCgiVar (1)) return; + + PyErr_Clear (); + +#if PY_MAJOR_VERSION >= 3 + DEBUGPYOBJ("->pByteCode", ccptr->pByteCode) +#else + DEBUGPYOBJ("->pByteCode", (struct _object*)ccptr->pByteCode) +#endif + + if (!ccptr->pMainDict2) + { + ccptr->pMainDict2 = PyObject_CallMethod(pMainDict, "copy", "()"); + if (!ccptr->pMainDict2) + { + ReportError (__LINE__, 0, 1, ErrorPython); + return; + } + DEBUGPYOBJ("ccptr->pMainDict2",ccptr->pMainDict2) + } + + pResult = PyEval_EvalCode (ccptr->pByteCode, ccptr->pMainDict2, + ccptr->pMainDict2); + DEBUGPYOBJ("pResult",pResult) + Py_XDECREF (pResult); + + /* new reference */ + if (pError = PyErr_Occurred()) + { + DEBUGPYOBJ("pError",pError) + Py_XDECREF (pError); + if (watches) WatchErrorCallout (); + error = ReportError (__LINE__, 0, 1, NULL); + } + + /* explicitly flush the buffer */ + pResult = PyObject_CallMethod (pStdout, "flush", NULL); + Py_XDECREF (pResult); + +#if PY_MAJOR_VERSION >= 3 + if (dbug) fprintf (stdout, "PyGILState_Check() %d\n", PyGILState_Check()); +#endif + + /* revert to the main thread */ + PyThreadState_Swap (pStateBuffer); + + if (error || !ReuseThread || !ccptr->FileNamePtr) + { + /* destroy the current thread */ + ccptr->pThreadState = FreshThread (ccptr->pThreadState, NULL); + ccptr->ThreadUsageCount = 0; + } + + + if (!SetupOsEnvCgiVar (0)) return; +} + +/*****************************************************************************/ +/* +Create the script byte-code by either loading the source code (.PY) and +compiling, or loading the pre-compiled byte code (.PYC) or optimised, +pre-compiled byte-code (.PYO). Reports it's own errors. Success is indicated +by '->pByteCode' containing a pointer to the code object, and failing by it +being NULL. +*/ + +void LoadByteCode +( +char *FileName, +char *ScriptName, +struct CodeCacheStruct *ccptr +) +{ + int fclen, status, + FileNameLength; + unsigned long magic, mtime; + char *cptr, *fcptr, *sptr, *zptr, + *CharPtr; + char FileNameByteCode [256]; + FILE *fp; + stat_t FstatBuffer; + struct CodeMetricStruct *cmptr; + + /*********/ + /* begin */ + /*********/ + + if (dbug) fprintf (stdout, "LoadByteCode() |%s|%s|\n", FileName, ScriptName); + + if (watches) WatchCallout ("LOAD %s %s", ScriptName, FileName); + + ccptr->LastUsedSecs = ccptr->ScriptMtimeSecs = ccptr->CodeUsageCount = 0; + + if (ccptr->pByteCode) + { + /* remove reference to any cached code object */ + Py_DECREF (ccptr->pByteCode); + ccptr->pByteCode = NULL; + } + + FlushCacheEntry (ccptr); + + zptr = (sptr = FileNameByteCode) + sizeof(FileNameByteCode); + for (cptr = FileName; *cptr && sptr < zptr; *sptr++ = *cptr++); + FileNameLength = cptr - FileName; + CharPtr = sptr; + /* first; try to open a '.pyo' (optimised byte-code) */ + *sptr++ = 'o'; + if (sptr > zptr) + { + ReportError (__LINE__, 0, 0, ErrorOverflow); + return; + } + *sptr = '\0'; + + fp = NULL; + for (;;) + { + if (dbug) fprintf (stdout, "|%s|\n", FileNameByteCode); + /* note; opened in 'binary' mode */ + fp = fopen (FileNameByteCode, "rb", "shr=get"); + if (fp && watches) WatchCallout ("BYTECODE %s", FileNameByteCode); + if (fp) break; + /* third; try to open a '.py' (source) */ + if (*CharPtr == 'c') break; + /* second; try to open a '.pyc' (compiled byte-code) */ + *CharPtr = 'c'; + } + if (!fp) + { + /* well, it's certainly not pre-compiled! */ + FileNameByteCode[0] = '\0'; + if (dbug) fprintf (stdout, "|%s|\n", FileName); + /* note; opened in 'record' mode */ + fp = fopen (FileName, "r", "ctx=rec", "shr=get"); + if (fp && watches) WatchCallout ("CODE %s", FileName); + if (!fp) + { + if (ProctorDetect ()) return; + status = vaxc$errno; + ReportError (__LINE__, status, 0, "Error opening !AZ", ScriptName); + return; + } + } + if (fstat (fileno(fp), &FstatBuffer) < 0) + { + status = vaxc$errno; + fclose (fp); + ReportError (__LINE__, status, 0, "Error fstat() !AZ", ScriptName); + return; + } + + PyErr_Clear (); + + if (FileNameByteCode[0]) + { + /* byte-code file; discard these first two longwords */ + magic = PyMarshal_ReadLongFromFile (fp); + mtime = PyMarshal_ReadLongFromFile (fp); + /* what's left is the byte-code */ +#if PY_MAJOR_VERSION >= 3 + ccptr->pByteCode = PyMarshal_ReadObjectFromFile (fp); +#else + ccptr->pByteCode = (PyCodeObject*)PyMarshal_ReadObjectFromFile (fp); +#endif + fclose (fp); + if (!ccptr->pByteCode) + { + ReportError (__LINE__, 0, 1, NULL); + return; + } + } + else + { + /* source code file */ + status = ReadFileIntoMemory (FileName, &fcptr, &fclen); + if (!(status & 1)) + { + ReportError (__LINE__, status, 0, NULL); + return; + } +#if PY_MAJOR_VERSION >= 3 + ccptr->pByteCode = Py_CompileString (fcptr, ScriptName, Py_file_input); +#else + ccptr->pByteCode = (PyCodeObject*)Py_CompileString (fcptr, ScriptName, + Py_file_input); +#endif + free (fcptr); + fclose (fp); + if (!ccptr->pByteCode) + { + ReportError (__LINE__, 0, 1, NULL); + return; + } + } + + /* successful load */ + ccptr->ScriptMtimeSecs = FstatBuffer.st_mtime; + ccptr->FileNamePtr = calloc (1, FileNameLength+1); + if (!ccptr->FileNamePtr) exit (vaxc$errno); + strcpy (ccptr->FileNamePtr, FileName); + ccptr->ScriptNamePtr = calloc (1, strlen(ScriptName)+1); + if (!ccptr->ScriptNamePtr) exit (vaxc$errno); + strcpy (ccptr->ScriptNamePtr, ScriptName); + if (FileNameByteCode[0]) + { + ccptr->FileNameByteCodePtr = calloc (1, FileNameLength+2); + if (!ccptr->FileNameByteCodePtr) exit (vaxc$errno); + strcpy (ccptr->FileNameByteCodePtr, FileNameByteCode); + } + if (dbug) + { + fprintf (stdout, "|%s|%s| %u", ccptr->FileNamePtr, + ccptr->FileNameByteCodePtr, ccptr->pByteCode); +#if PY_MAJOR_VERSION >= 3 + DEBUGPYOBJ("->pByteCode", ccptr->pByteCode) +#else + DEBUGPYOBJ("->pByteCode", (struct _object*)ccptr->pByteCode) +#endif + } +} + +/*****************************************************************************/ +/* +Check if this script (source or byte) code has been modified. +*/ + +int ByteCodeUpToDate (struct CodeCacheStruct *ccptr) + +{ + int retval; + stat_t StatBuffer; + + /*********/ + /* begin */ + /*********/ + + if (dbug) fprintf (stdout, "ByteCodeUpToDate()\n"); + + if (ccptr->FileNameByteCodePtr) + retval = stat (ccptr->FileNameByteCodePtr, &StatBuffer); + else + retval = stat (ccptr->FileNamePtr, &StatBuffer); + if (retval) return (0); + /* if the script file is more recent than the cached byte-code */ + if (dbug) fprintf (stdout, "%d - %d %d secs\n", + StatBuffer.st_mtime, ccptr->ScriptMtimeSecs, + StatBuffer.st_mtime - ccptr->ScriptMtimeSecs); + if (watches) WatchCallout ("MODIFIED %d secs", + StatBuffer.st_mtime - ccptr->ScriptMtimeSecs); + if (StatBuffer.st_mtime != ccptr->ScriptMtimeSecs) return (0); + return (1); +} + +/*****************************************************************************/ +/* +Free dynamic components, the thread, and reset the structure. */ void FlushCacheEntry (struct CodeCacheStruct *ccptr) @@ -1532,10 +1960,12 @@ if (dbug) fprintf (stdout, "FlushCacheEntry()\n"); - if (ccptr->FileNamePtr) - { - free (ccptr->FileNamePtr); - ccptr->FileNamePtr = NULL; - } - if (ccptr->FileNameByteCodePtr) + if (ccptr->FileNamePtr) free (ccptr->FileNamePtr); + if (ccptr->FileNameByteCodePtr) free (ccptr->FileNameByteCodePtr); + if (ccptr->ScriptNamePtr) free (ccptr->ScriptNamePtr); + if (ccptr->CodeMetricPtr) free (ccptr->CodeMetricPtr); + ccptr->FileNamePtr = ccptr->FileNameByteCodePtr = + ccptr->ScriptNamePtr = NULL; + ccptr->CodeMetricPtr = NULL; + if (ccptr->pByteCode) { @@ -1541,4 +1971,4 @@ { - free (ccptr->FileNameByteCodePtr); - ccptr->FileNameByteCodePtr = NULL; + Py_DECREF (ccptr->pByteCode); + ccptr->pByteCode = NULL; } @@ -1544,3 +1974,3 @@ } - if (ccptr->CodeMetricPtr) + if (ccptr->pMainDict2) { @@ -1546,6 +1976,11 @@ { - free (ccptr->CodeMetricPtr); - ccptr->CodeMetricPtr = NULL; + Py_XDECREF (ccptr->pMainDict2); + ccptr->pMainDict2 = NULL; + } + if (ccptr->pThreadState) + { + ccptr->pThreadState = FreshThread (ccptr->pThreadState, NULL); + ccptr->ThreadUsageCount = 0; } } @@ -1637,7 +2072,6 @@ /*****************************************************************************/ /* -For Python 3 covert an array of pointers to char to an array of pointers to -wide char. For Python 2 just return the original array of pointers to char. +Set the Python argv. */ @@ -1642,7 +2076,7 @@ */ -#if PY_MAJOR_VERSION >= 3 - -wchar_t** MungeArgv (int argc, char *argv[]) +#define VARRAY_SIZE 32 + +void SetArgv (char *ScriptName) { @@ -1647,10 +2081,11 @@ { - static wchar_t* wargv [32]; - - int idx; + int cnt, idx; + char *cptr; + char *pargv [VARRAY_SIZE]; + wchar_t* wargv [VARRAY_SIZE]; /*********/ /* begin */ /*********/ @@ -1652,18 +2087,40 @@ /*********/ /* begin */ /*********/ - if (dbug) fprintf (stdout, "MungeArgv()\n"); - - /* completely arbitrary */ - if (argc >= 32) exit (SS$_BUGCHECK); - - /* free any existing array of wide strings */ - for (idx = 0; idx < 32; idx++) + if (dbug) fprintf (stdout, "SetArgv() %d\n", MainArgc); + +#if PY_MAJOR_VERSION >= 3 + + if (MainArgc >= VARRAY_SIZE) exit (SS$_BUGCHECK); + + memset (wargv, 0, sizeof(wargv)); + + idx = 0; + if (ScriptName) wargv[idx++] = Py_DecodeLocale (ScriptName, NULL); + for (cnt = 1; cnt < MainArgc && idx < VARRAY_SIZE; cnt++) + { + if (dbug) fprintf (stdout, "%d |%s|\n", idx, MainArgv[cnt]); + if (!MainArgv[cnt]) break; + wargv[idx++] = Py_DecodeLocale (MainArgv[cnt], NULL); + } + + for (cnt = 0; + (cptr = Trn2Lnm ("PYRTE_ARGV", NULL, cnt)) && idx < VARRAY_SIZE; + cnt++) + { + if (dbug) fprintf (stdout, "%d |%s|\n", idx, cptr); + wargv[idx++] = Py_DecodeLocale (cptr, NULL); + } + + PySys_SetArgv (idx, wargv); + + /* free array of wide strings */ + for (idx = 0; idx < VARRAY_SIZE; idx++) { if (!wargv[idx]) continue; if (wargv[idx]) PyMem_RawFree (wargv[idx]); wargv[idx] = NULL; } @@ -1664,8 +2121,14 @@ { if (!wargv[idx]) continue; if (wargv[idx]) PyMem_RawFree (wargv[idx]); wargv[idx] = NULL; } - for (idx = 0; idx < argc; idx++) +#else /* PY_MAJOR_VERSION < 3 */ + + memset (pargv, 0, sizeof(pargv)); + + idx = 0; + if (ScriptName) pargv[idx++] = strdup (ScriptName); + for (cnt = 1; cnt < MainArgc && idx < VARRAY_SIZE; cnt++) { @@ -1671,6 +2134,6 @@ { - if (dbug) fprintf (stdout, "%d |%s|\n", idx, argv[idx]); - if (!argv[idx]) return (NULL); - wargv[idx] = Py_DecodeLocale (argv[idx], NULL); + if (dbug) fprintf (stdout, "%d |%s|\n", idx, MainArgv[cnt]); + if (!MainArgv[cnt]) break; + pargv[idx] = strdup (MainArgv[cnt]); } @@ -1675,26 +2138,21 @@ } - return (wargv); -} - -#else /* PY_MAJOR_VERSION < 3 */ - -char** MungeArgv (int argc, char *argv[]) - -{ - int idx; - - /*********/ - /* begin */ - /*********/ - - if (dbug) fprintf (stdout, "MungeArgv()\n"); - - if (dbug) - for (idx = 0; idx < argc; idx++) - fprintf (stdout, "%d |%s|\n", idx, argv[idx]); - - return (argv); -} + for (cnt = 0; + (cptr = Trn2Lnm ("PYRTE_ARGV", NULL, cnt)) && idx < VARRAY_SIZE; + cnt++) + { + if (dbug) fprintf (stdout, "%d |%s|\n", idx, cptr); + pargv[idx++] = strdup (cptr); + } + + PySys_SetArgv (idx, pargv); + + /* free array of strings */ + for (idx = 0; idx < VARRAY_SIZE; idx++) + { + if (!pargv[idx]) continue; + if (pargv[idx]) free (pargv[idx]); + pargv[idx] = NULL; + } #endif /* PY_MAJOR_VERSION >= 3 */ @@ -1699,5 +2157,6 @@ #endif /* PY_MAJOR_VERSION >= 3 */ +} /*****************************************************************************/ /* @@ -1701,10 +2160,6 @@ /*****************************************************************************/ /* -Create the script byte-code by either loading the source code (.PY) and -compiling, or loading the pre-compiled byte code (.PYC) or optimised, -pre-compiled byte-code (.PYO). Reports it's own errors. Success is indicated -by '->pByteCode' containing a pointer to the code object, and failing by it -being NULL. +Write data to WATCH [x]Script (useful for debugging). */ @@ -1709,4 +2164,4 @@ */ -void LoadByteCode +static PyObject* RteWATCH ( @@ -1712,6 +2167,5 @@ ( -char *FileName, -char *ScriptName, -struct CodeCacheStruct *ccptr +PyObject *self, +PyObject *args ) { @@ -1716,11 +2170,4 @@ ) { - int flen, status, - FileNameLength; - unsigned long magic, mtime; - char *cptr, *fptr, *sptr, *zptr, - *CharPtr; - char FileNameByteCode [256]; - FILE *fp; - stat_t FstatBuffer; + int DataLength; #if PY_MAJOR_VERSION >= 3 @@ -1726,3 +2173,3 @@ #if PY_MAJOR_VERSION >= 3 - struct _mod *pNode; + const char *DataPtr; #else @@ -1728,3 +2175,3 @@ #else - struct _node *pNode; + char *DataPtr; #endif @@ -1730,7 +2177,7 @@ #endif - struct CodeMetricStruct *cmptr; + PyObject *pValue; /*********/ /* begin */ /*********/ @@ -1732,34 +2179,13 @@ /*********/ /* begin */ /*********/ - if (dbug) fprintf (stdout, "LoadByteCode() |%s|\n", FileName); - - ccptr->LastUsedSecs = ccptr->ScriptMtimeSecs = ccptr->CodeUsageCount = 0; - - if (ccptr->pByteCode) - { - /* remove reference to any cached code object */ - Py_DECREF (ccptr->pByteCode); - ccptr->pByteCode = NULL; - } - - FlushCacheEntry (ccptr); - - zptr = (sptr = FileNameByteCode) + sizeof(FileNameByteCode); - for (cptr = FileName; *cptr && sptr < zptr; *sptr++ = *cptr++); - FileNameLength = cptr - FileName; - CharPtr = sptr; - /* first; try to open a '.pyo' (optimised byte-code) */ - *sptr++ = 'o'; - if (sptr > zptr) - { - ReportError (__LINE__, 0, 0, ErrorOverflow); - return; - } - *sptr = '\0'; - - fp = NULL; - for (;;) +/** + if (dbug) fprintf (stdout, "RteWATCH()\n"); + DEBUGPYOBJ("self",self) + DEBUGPYOBJ("args",args) +**/ + + if (!CgiPlusEscPtr) { @@ -1765,10 +2191,4 @@ { - if (dbug) fprintf (stdout, "|%s|\n", FileNameByteCode); - /* note; opened in 'binary' mode */ - fp = fopen (FileNameByteCode, "rb", "shr=get"); - if (fp) break; - /* third; try to open a '.py' (source) */ - if (*CharPtr == 'c') break; - /* second; try to open a '.pyc' (compiled byte-code) */ - *CharPtr = 'c'; + Py_INCREF (Py_None); + return (Py_None); } @@ -1774,3 +2194,6 @@ } - if (!fp) + + if (!PyArg_ParseTuple (args, "O", &pValue)) return (NULL); + + if (pValue == Py_None) { @@ -1776,14 +2199,6 @@ { - /* well, it's certainly not pre-compiled! */ - FileNameByteCode[0] = '\0'; - if (dbug) fprintf (stdout, "|%s|\n", FileName); - /* note; opened in 'record' mode */ - fp = fopen (FileName, "r", "ctx=rec", "shr=get"); - if (!fp) - { - if (ProctorDetect ()) return; - status = vaxc$errno; - ReportError (__LINE__, status, 0, "Error opening !AZ", ScriptName); - return; - } + AppOutputCount++; + fflush (stdout); + Py_INCREF (Py_None); + return (Py_None); } @@ -1789,16 +2204,3 @@ } - if (fstat (fileno(fp), &FstatBuffer) < 0) - { - status = vaxc$errno; - fclose (fp); - ReportError (__LINE__, status, 0, "Error fstat() !AZ", ScriptName); - return; - } - - if (FileNameByteCode[0]) - { - /* byte-code file; discard these first two longwords */ - magic = PyMarshal_ReadLongFromFile (fp); - mtime = PyMarshal_ReadLongFromFile (fp); - /* what's left is the byte-code */ + #if PY_MAJOR_VERSION >= 3 @@ -1804,3 +2206,3 @@ #if PY_MAJOR_VERSION >= 3 - ccptr->pByteCode = PyMarshal_ReadObjectFromFile (fp); + if (PyUnicode_Check (pValue)) #else @@ -1806,3 +2208,3 @@ #else - ccptr->pByteCode = (PyCodeObject*)PyMarshal_ReadObjectFromFile (fp); + if (PyString_Check (pValue)) #endif @@ -1808,10 +2210,2 @@ #endif - fclose (fp); - if (!ccptr->pByteCode) - { - ReportError (__LINE__, 0, 1, NULL); - return; - } - } - else { @@ -1817,9 +2211,2 @@ { - /* source code file */ - status = ReadFileIntoMemory (FileName, &fptr, &flen); - if (!(status & 1)) - { - ReportError (__LINE__, 0, 1, NULL); - return; - } #if PY_MAJOR_VERSION >= 3 @@ -1825,3 +2212,3 @@ #if PY_MAJOR_VERSION >= 3 - ccptr->pByteCode = Py_CompileString (fptr, ScriptName, Py_file_input); + DataPtr = PyUnicode_AsUTF8AndSize (pValue, &DataLength); #else @@ -1827,4 +2214,3 @@ #else - ccptr->pByteCode = (PyCodeObject*)Py_CompileString (fptr, ScriptName, - Py_file_input); + PyString_AsStringAndSize (pValue, &DataPtr, &DataLength); #endif @@ -1830,10 +2216,17 @@ #endif - free (fptr); - fclose (fp); - if (!ccptr->pByteCode) - { - ReportError (__LINE__, 0, 1, NULL); - return; - } + if (dbug) fprintf (stdout, "%d |%s|\n", DataLength, DataPtr); + + fflush (stdout); + fputs (CgiPlusEscPtr, stdout); + fflush (stdout); + /* the leading '!' indicates we're not going to read the response */ + fputs ("!WATCH: WATCH ", stdout); + fwrite (DataPtr, DataLength, 1, stdout); + fflush (stdout); + fputs (CgiPlusEotPtr, stdout); + fflush (stdout); + + Py_INCREF (Py_None); + return (Py_None); } @@ -1838,56 +2231,7 @@ } - /* successful load */ - ccptr->ScriptMtimeSecs = FstatBuffer.st_mtime; - ccptr->FileNamePtr = calloc (1, FileNameLength+1); - if (!ccptr->FileNamePtr) exit (vaxc$errno); - strcpy (ccptr->FileNamePtr, FileName); - if (FileNameByteCode[0]) - { - ccptr->FileNameByteCodePtr = calloc (1, FileNameLength+2); - if (!ccptr->FileNameByteCodePtr) exit (vaxc$errno); - strcpy (ccptr->FileNameByteCodePtr, FileNameByteCode); - } - if (dbug) - { - fprintf (stdout, "|%s|%s| %u", ccptr->FileNamePtr, - ccptr->FileNameByteCodePtr, ccptr->pByteCode); -#if PY_MAJOR_VERSION >= 3 - DEBUGPYOBJ("->pByteCode", ccptr->pByteCode) -#else - DEBUGPYOBJ("->pByteCode", (struct _object*)ccptr->pByteCode) -#endif - } -} - -/*****************************************************************************/ -/* -Check if this script (source or byte) code has been modified. -*/ - -int ByteCodeUpToDate (struct CodeCacheStruct *ccptr) - -{ - int retval; - stat_t StatBuffer; - - /*********/ - /* begin */ - /*********/ - - if (dbug) fprintf (stdout, "ByteCodeUpToDate()\n"); - - if (ccptr->FileNameByteCodePtr) - retval = stat (ccptr->FileNameByteCodePtr, &StatBuffer); - else - retval = stat (ccptr->FileNamePtr, &StatBuffer); - if (retval) return (0); - /* if the script file is more recent than the cached byte-code */ - if (dbug) fprintf (stdout, "|%d|%d|%s\n", - StatBuffer.st_mtime, ccptr->ScriptMtimeSecs, - StatBuffer.st_mtime == ccptr->ScriptMtimeSecs ? "YES" : "NO"); - if (StatBuffer.st_mtime != ccptr->ScriptMtimeSecs) return (0); - return (1); + PyErr_SetString (PyExc_TypeError, "type must be String or None"); + return (NULL); } /*****************************************************************************/ @@ -1903,4 +2247,7 @@ ) { int DataLength; +#if PY_MAJOR_VERSION >= 3 + const char *DataPtr; +#else char *DataPtr; @@ -1906,4 +2253,5 @@ char *DataPtr; +#endif PyObject *pValue; /*********/ @@ -1927,9 +2275,9 @@ } #if PY_MAJOR_VERSION >= 3 - if (PyBytes_Check (pValue)) + if (PyUnicode_Check (pValue)) #else if (PyString_Check (pValue)) #endif { #if PY_MAJOR_VERSION >= 3 @@ -1931,9 +2279,9 @@ #else if (PyString_Check (pValue)) #endif { #if PY_MAJOR_VERSION >= 3 - PyBytes_AsStringAndSize (pValue, &DataPtr, &DataLength); + DataPtr = PyUnicode_AsUTF8AndSize (pValue, &DataLength); #else PyString_AsStringAndSize (pValue, &DataPtr, &DataLength); #endif @@ -1966,6 +2314,7 @@ { static char RequestMethodGet[] = "REQUEST_METHOD=GET"; + static PyObject *pMainEnvironDict = NULL; int retval; char *cptr, *sptr, @@ -1977,10 +2326,9 @@ /* begin */ /*********/ - if (dbug) fprintf (stdout, "SetupOsEnvCgiVar() %d\n", InitEnv); - - if (!HeadCvtGet) - HeadCvtGet = (TrnLnm ("PYRTE_HEAD_CVT_GET", NULL) != NULL); - - if (InitEnv) + if (dbug) fprintf (stdout, "SetupOsEnvCgiVar(%d)\n", InitEnv); + + if (!HeadCvtGet) HeadCvtGet = (TrnLnm ("PYRTE_HEAD_CVT_GET") != NULL); + + if (!pMainEnvironDict) { @@ -1986,8 +2334,8 @@ { - /* borrowed reference */ - pOsEnvironDict = PyDict_GetItemString (pOsDict, "environ"); - if (!pOsEnvironDict) + /* get the "original" environment dictionary */ + pMainEnvironDict = PyDict_GetItemString (pOsDict, "environ"); + if (!pMainEnvironDict) { ReportError (__LINE__, 0, 1, ErrorCgiVarEnv); return (0); } @@ -1990,7 +2338,12 @@ { ReportError (__LINE__, 0, 1, ErrorCgiVarEnv); return (0); } - DEBUGPYOBJ("pOsEnvironDict",pOsEnvironDict) - + /* borrowed reference */ + Py_INCREF (pMainEnvironDict); + DEBUGPYOBJ("pMainEnvironDict",pMainEnvironDict) + } + + if (InitEnv) + { /* new reference */ @@ -1996,3 +2349,3 @@ /* new reference */ - pEnvironDict = PyObject_CallMethod(pOsEnvironDict, "copy", "()"); + pEnvironDict = PyObject_CallMethod(pMainEnvironDict, "copy", "()"); DEBUGPYOBJ("pEnvironDict",pEnvironDict) @@ -1998,4 +2351,3 @@ DEBUGPYOBJ("pEnvironDict",pEnvironDict) - while (cptr = CgiVar ("*")) { @@ -2000,20 +2352,5 @@ while (cptr = CgiVar ("*")) { - /** if (dbug) fprintf (stdout, "|%s|\n", cptr); **/ - - if ((toupper(*cptr) == 'F' && - !strncmp (cptr, "FORM_", 5)) || - (toupper(*cptr) == 'K' && - !strncmp (cptr, "KEY_", 4)) || - (toupper(*cptr) == 'P' && - !strcmp (cptr, "PYRTE")) || - (toupper(*cptr) == 'G' && - (!strncmp (cptr, "GATEWAY_EOF", 11) || - !strncmp (cptr, "GATEWAY_EOT", 11) || - !strncmp (cptr, "GATEWAY_ESC", 11) || - !strncmp (cptr, "GATEWAY_BG", 10)))) - continue; - if (HeadCvtGet && toupper(*cptr) == 'R' && !strcmp (cptr, "REQUEST_METHOD=HEAD")) @@ -2022,6 +2359,7 @@ for (sptr = cptr; *sptr && *sptr != '='; sptr++); if (!*sptr) continue; *sptr = '\0'; + if (dbug) fprintf (stdout, "|%s|\n", cptr); #if PY_MAJOR_VERSION >= 3 pValue = PyUnicode_FromString (sptr+1); #else @@ -2025,6 +2363,7 @@ #if PY_MAJOR_VERSION >= 3 pValue = PyUnicode_FromString (sptr+1); #else + /* wsgi complained it was not a string! */ pValue = PyString_FromString (sptr+1); #endif retval = PyMapping_SetItemString (pEnvironDict, cptr, pValue); @@ -2045,5 +2384,6 @@ return (0); } if (dbug) fprintf (stdout, "PyMapping_SetItemString() %d\n", retval); + DEBUGPYOBJ("pOsDict",pOsDict) } else @@ -2048,5 +2388,6 @@ } else + if (pEnvironDict) Py_DECREF (pEnvironDict); return (1); @@ -2114,10 +2455,10 @@ Python method to return the number of requests processed by this cached code. */ -static PyObject* RtePyMethUsageInterpreter () +static PyObject* RtePyMethUsageThread () { /*********/ /* begin */ /*********/ @@ -2118,12 +2459,12 @@ { /*********/ /* begin */ /*********/ - if (dbug) fprintf (stdout, "RtePyMethUsageInterpreter()\n"); - - return (Py_BuildValue ("i", CodeCachePtr->InterpreterUsageCount)); + if (dbug) fprintf (stdout, "RtePyMethUsageThread()\n"); + + return (Py_BuildValue ("i", CodeCachePtr->ThreadUsageCount)); } /*****************************************************************************/ @@ -2140,7 +2481,7 @@ if (dbug) fprintf (stdout, "RtePyMethUsageRte()\n"); - return (Py_BuildValue ("i", UsageCount)); + return (Py_BuildValue ("i", PyrteUsageCount)); } /*****************************************************************************/ @@ -2162,4 +2503,43 @@ /*****************************************************************************/ /* +Wait for next request. +*/ + +void CgiPlusBegin () + +{ + /*********/ + /* begin */ + /*********/ + + if (dbug) fprintf (stdout, "CgiPlusBegin()\n"); + + if (CgiPlusState) + { + ReportError (__LINE__, SS$_BUGCHECK, 0, NULL); + exit (SS$_BUGCHECK); + } + + /* first one (zeroeth) is always available */ + if (PyrteUsageCount++) CgiVar (""); + + CgiPlusState++; + DbugCheck (1); + + AppOutputCount = 0; + sys$gettim (&CurrentBinTime); + CurrentTimeSecs = decc$fix_time (&CurrentBinTime); + + RteScriptParam (); + + if (!SetupOsEnvCgiVar (1)) + { + ReportError (__LINE__, SS$_BUGCHECK, 0, NULL); + exit (SS$_BUGCHECK); + } +} + +/*****************************************************************************/ +/* Python method to wait for the next CGIplus request. @@ -2165,3 +2545,5 @@ Python method to wait for the next CGIplus request. -If not CGIplus (i.e. standard CGI) the function returns immediately. + +If an RTE caching or non-caching RTE execution just return True. + If a CGIplus request has been begun but not explicitly ended this function @@ -2167,12 +2549,17 @@ If a CGIplus request has been begun but not explicitly ended this function -provides the WASD script EOF sentinal. -It then blocks waiting for the next request. - -It accepts zero, one or two parameters. The first is a boolean (integer 0 or -1) that if True allows a standard CGI script to call this once before -returning False. The second parameter is also a boolean (integer 0 or 1). If -True it gets the script code modification time to check for changes. If -modified it returns False. +provides the WASD script EOF sentinal. It then blocks waiting for the next +request. + +If not CGIplus (i.e. standard CGI or RTE) the function returns True immediately +and if called again False. That is, a one-shot execution. The function then +returns to the caller which with RTE waits for the next request. With CGI +that's all folks. + +The function usage accepts zero, one or two parameters. The first is a boolean +(integer 0 or 1) that if True allows a standard CGI script to call this once +before returning False. The second parameter is also a boolean (integer 0 or +1). If True it gets the script code modification time to check for changes. +If modified it returns False. */ static PyObject* RtePyMethCgiPlusBegin @@ -2181,7 +2568,7 @@ PyObject *args ) { - static int CallCount = 0; + static int OneShot; int AllowSingleCGI = 0, CheckCodeMtime = 0; @@ -2191,14 +2578,9 @@ /* begin */ /*********/ - if (!self && !args) - { - /* special case (not Python) reset the call count */ - CallCount = 0; - return (NULL); - } - - if (dbug) fprintf (stdout, "RtePyMethCgiPlusBegin()\n"); + if (dbug) fprintf (stdout, "RtePyMethCgiPlusBegin() %u %u %d %d %d %d %d\n", + self, args, CgiPlusState, IsScriptRte, + IsCgiPlusScript, PyrteUsageCount, WsgiRunApp); if (WsgiRunApp) { @@ -2202,8 +2584,7 @@ if (WsgiRunApp) { - /* not available to WSGI applications */ Py_INCREF (Py_None); return (Py_None); } @@ -2206,6 +2587,6 @@ Py_INCREF (Py_None); return (Py_None); } - if (!IsCgiPlusScript) + if (!(IsScriptRte || IsCgiPlusScript)) { @@ -2211,2 +2592,3 @@ { + /* call from Python script inside standard CGI; allow one off */ if (!PyArg_ParseTuple (args, "|i", &AllowSingleCGI)) return (NULL); @@ -2212,4 +2594,5 @@ if (!PyArg_ParseTuple (args, "|i", &AllowSingleCGI)) return (NULL); - - if (AllowSingleCGI) + if (dbug) fprintf (stdout, "AllowSingleCGI:%d\n", AllowSingleCGI); + + if (!OneShot && AllowSingleCGI) { @@ -2215,8 +2598,6 @@ { - if (!CallCount++) - { - Py_INCREF(Py_True); - return (Py_True); - } + OneShot = 1; + Py_INCREF(Py_True); + return (Py_True); } @@ -2221,6 +2602,7 @@ } + OneShot = 0; Py_INCREF(Py_False); return (Py_False); } @@ -2223,6 +2605,6 @@ Py_INCREF(Py_False); return (Py_False); } - if (CgiPlusPythonEnd) + if (IsCgiPlusScript) { @@ -2228,27 +2610,10 @@ { - if (!IsCgiPlusScript) - { - Py_INCREF(Py_False); - return (Py_False); - } - - fflush (stdout); - fputs (CgiPlusEofPtr, stdout); - fflush (stdout); - - if (!PyArg_ParseTuple (args, "|ii", &AllowSingleCGI, &CheckCodeMtime)) - return (NULL); - - if (CheckCodeMtime) - { - if (!ByteCodeUpToDate (CodeCachePtr)) - { - Py_INCREF(Py_False); - return (Py_False); - } - } - - CgiPlusPythonEnd = 0; - SetupOsEnvCgiVar (0); + if (!CgiPlusState) + /* wait for next request */ + CgiPlusBegin (); + else + CgiPlusEnd (); + Py_INCREF(Py_True); + return (Py_True); } @@ -2253,18 +2618,5 @@ } - /* block waiting for the next request (if not the first usage) */ - if (CallCount++) CgiVar (""); - - UsageCount++; - CodeCachePtr->CodeUsageCount++; - CgiPlusPythonEnd = 1; - AppOutputCount = 0; - - DbugCheck(); - - RteScriptParam (); - - if (!NoStreamMode) fprintf (stdout, "Script-Control: X-stream-mode\n"); - - if (!SetupOsEnvCgiVar (1)) + /* while wasd.cgiplus_begin(True): first is True then False */ + if (CgiPlusState++ == 1) { @@ -2270,4 +2622,11 @@ { + /* first True */ + Py_INCREF(Py_True); + return (Py_True); + } + else + { + /* then False */ Py_INCREF(Py_False); return (Py_False); } @@ -2271,9 +2630,36 @@ Py_INCREF(Py_False); return (Py_False); } - - Py_INCREF(Py_True); - return (Py_True); +} + +/*****************************************************************************/ +/* +Provide the end-of-request sentinal. +*/ + +void CgiPlusEnd () + +{ + /*********/ + /* begin */ + /*********/ + + if (dbug) fprintf (stdout, "CgiPlusEnd() %d\n", CgiPlusState); + + if (!CgiPlusState) + { + ReportError (__LINE__, SS$_BUGCHECK, 0, NULL); + exit (SS$_BUGCHECK); + } + + DbugCheck (0); + CgiPlusState = 0; + + fflush (stdout); + fputs (CgiPlusEofPtr, stdout); + fflush (stdout); + + SetupOsEnvCgiVar (0); } /*****************************************************************************/ @@ -2299,11 +2685,19 @@ /* begin */ /*********/ - if (dbug) fprintf (stdout, "RtePyMethCgiPlusEnd()\n"); - - if (!IsCgiPlusScript) + if (dbug) fprintf (stdout, "RtePyMethCgiPlusEnd() %u %u %d\n", + self, args, CgiPlusState); + + if (WsgiRunApp) + { + /* not available to WSGI applications */ + Py_INCREF (Py_None); + return (Py_None); + } + + if (!IsCgiPlusScript || !CgiPlusState) { Py_INCREF(Py_False); return (Py_False); } @@ -2305,14 +2699,15 @@ { Py_INCREF(Py_False); return (Py_False); } - fflush (stdout); - fputs (CgiPlusEofPtr, stdout); - fflush (stdout); - - CgiPlusPythonEnd = 0; - SetupOsEnvCgiVar (0); + if (!CgiPlusState) + { + Py_INCREF(Py_False); + return (Py_False); + } + + CgiPlusEnd (); if (!PyArg_ParseTuple (args, "|i", &CheckCodeMtime)) return (NULL); @@ -2362,7 +2757,7 @@ { /* with plain old CGI might not already be open */ if (!CgiPlusInFp) - if (!(CgiPlusInFp = fopen (TrnLnm("CGIPLUSIN", NULL), "r"))) + if (!(CgiPlusInFp = fopen (TrnLnm("CGIPLUSIN"), "r"))) exit (vaxc$errno); rewind (CgiPlusInFp); @@ -2376,5 +2771,4 @@ { /* excise the (CRTL) trailing newline */ if (retval) retval--; -#if PY_MAJOR_VERSION >= 3 pReturn = PyUnicode_FromStringAndSize (buf, retval); @@ -2380,7 +2774,4 @@ pReturn = PyUnicode_FromStringAndSize (buf, retval); -#else - pReturn = PyString_FromStringAndSize (buf, retval); -#endif } } @@ -2422,5 +2813,4 @@ { /* excise the (CRTL) trailing newline */ if (retval) retval--; -#if PY_MAJOR_VERSION >= 3 pReturn = PyUnicode_FromStringAndSize (bptr, retval); @@ -2426,7 +2816,4 @@ pReturn = PyUnicode_FromStringAndSize (bptr, retval); -#else - pReturn = PyString_FromStringAndSize (bptr, retval); -#endif } free (bptr); @@ -2436,7 +2823,7 @@ /*****************************************************************************/ /* -Set whether the Python interpreter being used is disposed of at the end of the +Set whether the Python thread being used is disposed of at the end of the current request (0 or False) or resued for the next request (1 or True). */ @@ -2440,7 +2827,7 @@ current request (0 or False) or resued for the next request (1 or True). */ -static PyObject* RtePyMethReuseInterpreter +static PyObject* RtePyMethReuseThread ( PyObject *self, PyObject *args @@ -2450,9 +2837,9 @@ /* begin */ /*********/ - if (dbug) fprintf (stdout, "RtePyMethReuseInterpreter()\n"); - - if (!PyArg_ParseTuple (args, "i", &ReuseInterpreter)) return (NULL); + if (dbug) fprintf (stdout, "RtePyMethReuseThread()\n"); + + if (!PyArg_ParseTuple (args, "i", &ReuseThread)) return (NULL); Py_INCREF(Py_None); return (Py_None); } @@ -2501,7 +2888,7 @@ if (dbug) fprintf (stdout, "RtePyMethRteCacheEntry()\n"); - if (!TrnLnm ("PYRTE_CACHE_ENTRY", NULL)) + if (!TrnLnm ("PYRTE_CACHE_ENTRY")) { PyErr_SetString (PyExc_RuntimeError, "logical name PYRTE_CACHE_ENTRY not defined"); @@ -2524,9 +2911,9 @@ sprintf (StringBuf, "%u,%u,%u,%u,%s,%s,%u,%u", CodeCache[idx].CodeUsageCount, - CodeCache[idx].InterpreterUsageCount, + CodeCache[idx].ThreadUsageCount, CodeCache[idx].LastUsedSecs, CodeCache[idx].ScriptMtimeSecs, CodeCache[idx].FileNamePtr, CodeCache[idx].FileNameByteCodePtr ? CodeCache[idx].FileNameByteCodePtr : "(null)", @@ -2528,9 +2915,9 @@ CodeCache[idx].LastUsedSecs, CodeCache[idx].ScriptMtimeSecs, CodeCache[idx].FileNamePtr, CodeCache[idx].FileNameByteCodePtr ? CodeCache[idx].FileNameByteCodePtr : "(null)", - (unsigned long)CodeCache[idx].pInterpState, + (unsigned long)CodeCache[idx].pThreadState, (unsigned long)CodeCache[idx].pByteCode); idx++; @@ -2852,5 +3239,4 @@ cptr = CgiVar ("REQUEST_SCHEME"); if (!*cptr || !strcmp (cptr, "http:")) -#if PY_MAJOR_VERSION >= 3 pValue = PyUnicode_FromString ("http"); @@ -2856,5 +3242,2 @@ pValue = PyUnicode_FromString ("http"); -#else - pValue = PyString_FromString ("http"); -#endif else @@ -2860,3 +3243,2 @@ else -#if PY_MAJOR_VERSION >= 3 pValue = PyUnicode_FromString ("https"); @@ -2862,7 +3244,4 @@ pValue = PyUnicode_FromString ("https"); -#else - pValue = PyString_FromString ("https"); -#endif PyMapping_SetItemString (pEnvironDict, "wsgi.url_scheme", pValue); Py_DECREF(pValue); @@ -3022,5 +3401,4 @@ BodyLength = cptr - BodyPtr; if (BodyLength == 0) -#if PY_MAJOR_VERSION >= 3 pBody = PyUnicode_FromString(""); @@ -3026,6 +3404,3 @@ pBody = PyUnicode_FromString(""); -#else - pBody = PyString_FromString(""); -#endif else if (BodyLength == strlen (BodyPtr)) @@ -3030,4 +3405,3 @@ else if (BodyLength == strlen (BodyPtr)) -#if PY_MAJOR_VERSION >= 3 pBody = PyUnicode_FromString (BodyPtr); @@ -3033,5 +3407,2 @@ pBody = PyUnicode_FromString (BodyPtr); -#else - pBody = PyString_FromString (BodyPtr); -#endif else @@ -3037,3 +3408,2 @@ else -#if PY_MAJOR_VERSION >= 3 pBody = PyUnicode_FromStringAndSize (BodyPtr, BodyLength); @@ -3039,7 +3409,4 @@ pBody = PyUnicode_FromStringAndSize (BodyPtr, BodyLength); -#else - pBody = PyString_FromStringAndSize (BodyPtr, BodyLength); -#endif if (BodyPtr) free (BodyPtr); @@ -3072,6 +3439,6 @@ WsgiForceBuffering = 1; else if (!strncasecmp (cptr, "/reuse", 6)) - ReuseInterpreter = 1; + ReuseThread = 1; else if (!strncasecmp (cptr, "/noreuse", 8)) @@ -3076,6 +3443,6 @@ else if (!strncasecmp (cptr, "/noreuse", 8)) - ReuseInterpreter = 0; + ReuseThread = 0; else if (!strncasecmp (cptr, "/nostream", 9)) NoStreamMode = 1; @@ -3093,7 +3460,7 @@ /* */ -void DbugCheck () +int DbugCheck (int enable) { char *cptr, *sptr; @@ -3101,12 +3468,7 @@ /* begin */ /*********/ - if (!GlobalDebug) - if (cptr = TrnLnm ("PYRTE$DBUG", NULL)) - if (!strchr (cptr, '*')) - if (sptr = CgiVar ("SCRIPT_NAME")) - if (!strstr (sptr, cptr)) - cptr = NULL; - - if ((dbug = GlobalDebug) || (dbug = (cptr != NULL))) + if (!enable) return (dbug = 0); + + if (cptr = TrnLnm ("PYRTE$DBUG")) { @@ -3112,6 +3474,15 @@ { - fprintf (stdout, + dbug = 1; + if (!strchr (cptr, '*')) + if (sptr = CgiVar ("SCRIPT_NAME")) + if (!strstr (sptr, cptr)) + dbug = 0; + } + + if (!dbug) return (0); + + fprintf (stdout, "Script-Control: X-record-mode\n\ Content-Type: text/plain\n\ \n\ ***** %s (%s) Python %s *****\n", @@ -3114,13 +3485,10 @@ "Script-Control: X-record-mode\n\ Content-Type: text/plain\n\ \n\ ***** %s (%s) Python %s *****\n", - SoftwareId, BUILD_DATETIME, Py_GetVersion()); -fprintf (stdout, "+++++ %d %d\n",cptr,sptr); - fflush (stdout); - NoStreamMode = 1; - } - + SoftwareId, BUILD_DATETIME, Py_GetVersion()); + fflush (stdout); + NoStreamMode = 1; } /****************************************************************************/ @@ -3226,10 +3594,7 @@ } if (dbug) - if (BufferCount < 50000) - fprintf (stdout, "%d |%s|\n", BufferCount, BufferPtr); - else - fprintf (stdout, "%d || TOO LARGE!\n", BufferCount); + fprintf (stdout, "%d |%s|\n", BufferCount, BufferPtr); if (FileTextPtr != NULL) *FileTextPtr = BufferPtr; if (FileSizePtr != NULL) *FileSizePtr = BufferCount; @@ -3281,7 +3646,7 @@ Make it look a bit like the standard WASD error message. */ -void ReportError +int ReportError ( int SourceCodeLine, int VmsStatus, @@ -3290,4 +3655,7 @@ ... ) { + static int ErrorReported; + static FILE *CgiPlusIn; + int argcnt, status, @@ -3293,5 +3661,7 @@ int argcnt, status, - CgiTbLoaded; + CgiLoaded, + CgiTbLoaded, + HttpStatus; unsigned short ShortLen; char *cptr, *sptr; char Buffer [2048], @@ -3309,9 +3679,18 @@ /* begin */ /*********/ - if (dbug) fprintf (stdout, "ReportError() %d\n", SourceCodeLine); - - CgiTbLoaded = 0; + if (dbug) fprintf (stdout, "ReportError() %d %%X%08.08X %d\n", + SourceCodeLine, VmsStatus, PythonErrorPrint); + + if (!SourceCodeLine) + { + /* return if reported error while resetting */ + status = ErrorReported; + ErrorReported = 0; + return (status); + } + + CgiLoaded = CgiTbLoaded = 0; if (Py_IsInitialized()) { /* assume that if it's loaded it's also enabled */ @@ -3322,7 +3701,6 @@ CgiTbLoaded = 1; Py_DECREF (pObject); } - if (dbug) fprintf (stdout, "pObject:%u CgiTbLoaded:%d\n", - pObject, CgiTbLoaded); - if (CgiTbLoaded) + pObject = PyUnicode_FromString ("cgi"); + if (pObject) { @@ -3328,10 +3706,6 @@ { - if (PythonErrorPrint) - { - PyErr_Print(); - fprintf (stdout, "<!-- PyRTE %s LINE:%d -->\n", - SoftwareId, SourceCodeLine); - return; - } + if (pMainDict && PyDict_Contains (pMainDict, pObject) == 1) + CgiLoaded = 1; + Py_DECREF (pObject); } } @@ -3336,5 +3710,38 @@ } } + if (dbug) fprintf (stdout, "CgiLoaded:%d CgiTbLoaded:%d\n", + CgiLoaded, CgiTbLoaded); + + ErrorReported = 1; + + HttpStatus = 0; + if (ServerSoftware() >= 110502) + { + /* for WASD v11.5.2 and later detect if there has been a response */ + if (!CgiPlusIn) + if (!(CgiPlusIn = fopen ("CGIPLUSIN:", "r"))) + exit (vaxc$errno); + + fflush (stdout); + fputs (CgiPlusEscPtr, stdout); + fflush (stdout); + fputs ("HTTP-STATUS:", stdout); + fflush (stdout); + fgets (Buffer, sizeof(Buffer), CgiPlusIn); + fputs (CgiPlusEotPtr, stdout); + fflush (stdout); + if (atoi(Buffer) == 200) + HttpStatus = atoi(Buffer+4); + } + + if (CgiTbLoaded) + { + if (!HttpStatus) + fputs ("Status: 502\nContent-Type: text/html\n\n", stdout); + PyErr_Print(); + fprintf (stdout, "<!-- PyRTE LINE:%d -->\n", SourceCodeLine); + return (ErrorReported); + } va_count (argcnt); vecptr = FaoVector; @@ -3360,9 +3767,8 @@ if (AppOutputCount) { - fprintf (stdout, "\n<!-- PyRTE %s LINE:%d -->\n", - SoftwareId, SourceCodeLine); + fprintf (stdout, "\n<!-- PyRTE LINE:%d -->\n", SourceCodeLine); if (VmsStatus) fprintf (stdout, "<!-- %%X%08.08X -->\n", VmsStatus); if (!CgiTbLoaded) fputs ("<pre>\n----------\n", stdout); PyErr_Print(); if (!CgiTbLoaded) fputs ("----------\n</pre>\n", stdout); @@ -3365,7 +3771,7 @@ if (VmsStatus) fprintf (stdout, "<!-- %%X%08.08X -->\n", VmsStatus); if (!CgiTbLoaded) fputs ("<pre>\n----------\n", stdout); PyErr_Print(); if (!CgiTbLoaded) fputs ("----------\n</pre>\n", stdout); - return; + return (ErrorReported); } @@ -3370,8 +3776,10 @@ } - fputs ("Status: 502\nContent-Type: text/html\n\n", stdout); - - fprintf (stdout, + if (!HttpStatus) + { + fputs ("Status: 502\nContent-Type: text/html\n\n", stdout); + + fprintf (stdout, "<!DOCTYPE html>\n\ <html>\n\ <head>\n\ @@ -3382,8 +3790,9 @@ <body>\n\ <font size=\"+1\"><b>ERROR 502</b> - \ External agent did not respond (or not acceptably)</font>\n", - SoftwareId, - SourceCodeLine); + SoftwareId, + SourceCodeLine); + } if (VmsStatus) fprintf (stdout, "<!-- %%X%08.08X -->\n", VmsStatus); @@ -3404,6 +3813,8 @@ </body>\n\ </html>\n", cptr ? cptr : "", cptr ? "\n" : ""); + + return (ErrorReported); } /*****************************************************************************/ @@ -3432,9 +3843,8 @@ static int CalloutDone, StructLength; - static char *EmptyScriptNamePtr = NULL, - *NextVarNamePtr; + static char *NextVarNamePtr; static char StructBuffer [CGIVAR_STRUCT_SIZE]; int status; int Length; @@ -3437,8 +3847,8 @@ static char StructBuffer [CGIVAR_STRUCT_SIZE]; int status; int Length; - char *bptr, *cptr, *sptr; + char *bptr, *cptr, *sptr, *vptr; /*********/ /* begin */ @@ -3474,15 +3884,9 @@ sptr = (NextVarNamePtr += SOUS); NextVarNamePtr += Length; /* by default CGI variable names are prefixed by "WWW_", ignore */ - sptr += 4; - /* ensure an empty SCRIPT_NAME *is* empty */ - if (toupper(*sptr) != 'S') return (sptr); - if (toupper(*(sptr+1)) != 'C') return (sptr); - if (!EmptyScriptName (sptr, NULL)) return (sptr); - if (EmptyScriptNamePtr) return (EmptyScriptNamePtr); - EmptyScriptNamePtr = calloc (1, 13); - if (EmptyScriptNamePtr) strcpy (EmptyScriptNamePtr, "SCRIPT_NAME="); - return (EmptyScriptNamePtr); + if (*(unsigned long*)sptr == 'WWW_') sptr += 4; + sptr = EmptyScriptName (sptr); + return (sptr); } /* standard CGI */ @@ -3498,9 +3902,9 @@ for (bptr=StructBuffer; Length=*(unsigned short*)bptr; bptr+=Length) { /* by default CGI variable names are prefixed by "WWW_", ignore */ - sptr = (bptr += SOUS) + 4; + sptr = vptr = (bptr += SOUS) + 4; for (cptr = VarName; *cptr && *sptr && *sptr != '='; cptr++, sptr++) if (toupper(*cptr) != toupper(*sptr)) break; /* if found return a pointer to the value */ if (!*cptr && *sptr == '=') { @@ -3502,15 +3906,10 @@ for (cptr = VarName; *cptr && *sptr && *sptr != '='; cptr++, sptr++) if (toupper(*cptr) != toupper(*sptr)) break; /* if found return a pointer to the value */ if (!*cptr && *sptr == '=') { - sptr++; - /* ensure an empty SCRIPT_NAME *is* empty */ - if (toupper(*sptr) != 'S') return (sptr); - if (toupper(*(sptr+1)) != 'C') return (sptr); - if (!EmptyScriptName (VarName, sptr)) return (sptr); - while (*sptr) sptr++; - return (sptr); + sptr = EmptyScriptName (vptr); + return (sptr + ((cptr - VarName) + 1)); } } /* not found */ @@ -3534,7 +3933,7 @@ /* the CGIPLUSIN stream can be left open */ if (!CgiPlusInFp) - if (!(CgiPlusInFp = fopen (TrnLnm("CGIPLUSIN", NULL), "r"))) + if (!(CgiPlusInFp = fopen (TrnLnm("CGIPLUSIN"), "r"))) exit (vaxc$errno); /* get the starting record (the essentially discardable one) */ @@ -3538,6 +3937,7 @@ exit (vaxc$errno); /* get the starting record (the essentially discardable one) */ + DbugCheck (0); for (;;) { cptr = fgets (StructBuffer, sizeof(StructBuffer), CgiPlusInFp); @@ -3607,6 +4007,8 @@ CalloutDone = 1; } + DbugCheck (1); + return (NULL); # undef SOUS @@ -3678,6 +4080,7 @@ NULL }; static int SymbolIndex = 0; - static char WwwName [256] = "WWW_"; - static $DESCRIPTOR (NameDsc, WwwName); + static char *wptr = NULL; + static char SymbolBuffer [2048]; + static $DESCRIPTOR (NameDsc, SymbolBuffer); static $DESCRIPTOR (ValueDsc, ""); @@ -3683,3 +4086,4 @@ static $DESCRIPTOR (ValueDsc, ""); + static $DESCRIPTOR (WwwServerDsc, "WWW_SERVER_SOFTWARE"); int status; @@ -3684,10 +4088,9 @@ int status; - unsigned short ShortLength; - char *cptr; - char Value [1024]; + unsigned short slen; + char *cptr, *sptr, *vnptr, *zptr; /*********/ /* begin */ /*********/ @@ -3689,8 +4092,17 @@ /*********/ /* begin */ /*********/ - if (dbug) fprintf (stdout, "CgiVarDclSymbol()\n"); +// if (dbug) fprintf (stdout, "CgiVarDclSymbol() |%s|\n", VarName); + + if (!wptr) + { + status = lib$get_symbol (&WwwServerDsc, &ValueDsc, &slen, NULL); + if (status & 1) + wptr = "WWW_"; + else + wptr = ""; + } if (VarName[0] == '*') @@ -3695,5 +4107,5 @@ if (VarName[0] == '*') - cptr = SymbolNames[SymbolIndex++]; + vnptr = SymbolNames[SymbolIndex++]; else { @@ -3698,6 +4110,6 @@ else { - cptr = VarName; + vnptr = VarName; SymbolIndex = 0; } @@ -3701,5 +4113,5 @@ SymbolIndex = 0; } - while (cptr) + while (vnptr) { @@ -3705,7 +4117,11 @@ { - /* by default WASD CGI variable names are prefixed by "WWW_", add */ - strncpy (WwwName+4, cptr, sizeof(WwwName)-5); - NameDsc.dsc$w_length = strlen(WwwName); - ValueDsc.dsc$a_pointer = Value; - ValueDsc.dsc$w_length = sizeof(Value)-1; + zptr = (sptr = SymbolBuffer) + sizeof(SymbolBuffer)-1; + if (*wptr) for (cptr = wptr; *cptr && sptr < zptr; *sptr++ = *cptr++); + for (cptr = vnptr; *cptr && sptr < zptr; *sptr++ = *cptr++); + NameDsc.dsc$a_pointer = SymbolBuffer; + NameDsc.dsc$w_length = sptr - SymbolBuffer; + if (sptr < zptr) *sptr++ = '='; + *sptr = '\0'; + ValueDsc.dsc$a_pointer = sptr; + ValueDsc.dsc$w_length = (sizeof(SymbolBuffer)-1) - (sptr - SymbolBuffer); @@ -3711,4 +4127,4 @@ - status = lib$get_symbol (&NameDsc, &ValueDsc, &ShortLength, NULL); + status = lib$get_symbol (&NameDsc, &ValueDsc, &slen, NULL); if (status & 1) { @@ -3713,13 +4129,13 @@ if (status & 1) { - /* ensure an empty SCRIPT_NAME *is* empty */ - if (toupper(VarName[0]) != 'S' && EmptyScriptName (VarName, Value)) - ShortLength = 0; - cptr = calloc (1, ShortLength+1); - memcpy (cptr, Value, ShortLength); - cptr[ShortLength] = '\0'; - return (cptr); + sptr[slen] = '\0'; + sptr = strdup (*wptr ? SymbolBuffer+4 : SymbolBuffer); + sptr = EmptyScriptName (sptr); + if (VarName[0] == '*') return (sptr); + while (*sptr && *sptr != '=') sptr++; + if (*sptr) sptr++; + return (sptr); } if (VarName[0] != '*') return (NULL); /* assume the failure was 'no such symbol' and look further */ @@ -3722,8 +4138,8 @@ } if (VarName[0] != '*') return (NULL); /* assume the failure was 'no such symbol' and look further */ - cptr = SymbolNames[SymbolIndex++]; + vnptr = SymbolNames[SymbolIndex++]; } SymbolIndex = 0; @@ -3733,6 +4149,5 @@ /*****************************************************************************/ /* Suppress the WASD-ism of an empty or 'root' script name (i.e. "/"). -If 'VarValue' is none then 'VarName' is "NAME=<value>" string. */ @@ -3737,8 +4152,5 @@ */ -int EmptyScriptName -( -char *VarName, -char *VarValue -) +char* EmptyScriptName (char *sptr) + { @@ -3744,8 +4156,5 @@ { - char *cptr = "SCRIPT_NAME", - *sptr = VarName; - /*********/ /* begin */ /*********/ @@ -3748,24 +4157,12 @@ /*********/ /* begin */ /*********/ - if (dbug) fprintf (stdout, "EmptyScriptName() %s %s\n", VarName, VarValue); - - while (*cptr && *sptr && *sptr != '=') - { - if (*cptr != toupper(*sptr)) break; - cptr++; - sptr++; - } - if (*cptr || (*sptr && *sptr != '=')) return (0); - if (VarValue) - { - if (*(unsigned short*)VarValue != '/\0') return (0); - if (!*VarValue) return (0); - return (1); - } - if (*(unsigned short*)(sptr+1) != '/\0') return (0); - return (1); +// if (dbug) fprintf (stdout, "EmptyScriptName() |%s|\n", sptr); + + if (*(unsigned long*)sptr != 'SCRI') return (sptr); + if (!strncmp (sptr, "SCRIPT_NAME=/\0", 14)) return ("SCRIPT_NAME="); + return (sptr); } /*****************************************************************************/ @@ -3855,7 +4252,7 @@ <p>%d Item(s)\n", SoftwareId, BUILD_DATETIME, SoftwareId, pyver, - ProcessPid, UsageCount, + ProcessPid, PyrteUsageCount, CodeCacheCount); if (CodeCacheCount) @@ -4175,7 +4572,7 @@ cmptr->JpiCpuTim[midx] = JpiCpuTim; cmptr->JpiDirIO[midx] = JpiDirIO; cmptr->JpiPageFlts[midx] = JpiPageFlts; - cmptr->UsageNumber[midx] = UsageCount; + cmptr->UsageNumber[midx] = PyrteUsageCount; zptr = (sptr = cmptr->RequestPath[midx]) + sizeof(cmptr->RequestPath[midx])-1; @@ -4255,7 +4652,7 @@ { static $DESCRIPTOR (FaoDsc, -"REAL:!%T CPU:!UL.!2ZL DIO:!UL BIO:!UL FAULTS:!UL\0"); +"REAL:!%T CPU:!UL.!2ZL DIO:!UL BIO:!UL FAULTS:!UL PGFL:!UL/!UL%\0"); static char StatString [96]; static $DESCRIPTOR (StatStringDsc, StatString); @@ -4266,7 +4663,22 @@ LibStatTimerDio = 4, LibStatTimerFaults = 5; - int status; + static unsigned long JpiPagFilCnt, + JpiPgFlQuo; + static struct + { + unsigned short buf_len; + unsigned short item; + void *buf_addr; + void *ret_len; + } JpiItems [] = + { + { sizeof(JpiPagFilCnt), JPI$_PAGFILCNT, &JpiPagFilCnt, 0 }, + { sizeof(JpiPgFlQuo), JPI$_PGFLQUOTA, &JpiPgFlQuo, 0 }, + { 0,0,0,0 } + }; + + int percent, status; unsigned long CpuBinTime, CountBio, CountDio, @@ -4283,6 +4695,10 @@ return (NULL); } + status = sys$getjpiw (0, 0, 0, &JpiItems, 0, 0, 0); + if (!(status & 1)) exit (status); + percent = 100 - ((JpiPagFilCnt * 100) / JpiPgFlQuo); + /* post-processing reset */ lib$stat_timer (&LibStatTimerReal, &RealBinTime, 0); lib$stat_timer (&LibStatTimerCpu, &CpuBinTime, 0); @@ -4291,10 +4707,10 @@ lib$stat_timer (&LibStatTimerFaults, &CountFaults, 0); sys$fao (&FaoDsc, 0, &StatStringDsc, &RealBinTime, CpuBinTime/100, CpuBinTime%100, - CountDio, CountBio, CountFaults); + CountDio, CountBio, CountFaults, JpiPagFilCnt, percent); return (StatString); } /*****************************************************************************/ /* @@ -4295,12 +4711,52 @@ return (StatString); } /*****************************************************************************/ /* -Translate a logical name using LNM$FILE_DEV. Returns a pointer to the value -string, or NULL if the name does not exist. If 'LogValue' is supplied the -logical name is translated into that (assumed to be large enough), otherwise -it's translated into an internal static buffer. +Using the WATCH [x]Script or PYRTE_DBUG or PYRTE_STAT_TIMER logical names. +*/ + +void StatTimerCallout (int count) + +{ + fflush (stdout); + fputs (CgiPlusEscPtr, stdout); + fflush (stdout); + /* the leading '!' indicates we're not going to read the response */ + fprintf (stdout, "!WATCH: %s USAGE:%d/%d %s\n", + SoftwareId, count, PyrteUsageCount, StatTimer(TRUE)); + fflush (stdout); + fputs (CgiPlusEotPtr, stdout); + fflush (stdout); +} + +/*****************************************************************************/ +/* +Using the WATCH [x]Script formatted debug output. +*/ + +void WatchCallout (char *fmt, ...) + +{ + va_list args; + + fflush (stdout); + fputs (CgiPlusEscPtr, stdout); + fflush (stdout); + /* the leading '!' indicates we're not going to read the response */ + fputs ("!WATCH: ", stdout); + va_start (args, fmt); + vfprintf (stdout, fmt, args); + va_end(args); + fflush (stdout); + fputs (CgiPlusEotPtr, stdout); + fflush (stdout); +} + +/*****************************************************************************/ +/* +Return an integer representing the server sooftware version. +"HTTPd-WASD/11.5.2" should become 110502. */ @@ -4305,5 +4761,69 @@ */ -char* TrnLnm +int ServerSoftware (void) + +{ + static int ServerSoftware; + char *cptr; + + if (ServerSoftware) return (ServerSoftware); + if (!(cptr = CgiVar ("SERVER_SOFTWARE"))) + return (ServerSoftware = 0); + while (*cptr && !isdigit(*cptr)) cptr++; + ServerSoftware = atoi(cptr) * 10000; + while (*cptr && isdigit(*cptr)) cptr++; + if (*cptr == '.') cptr++; + ServerSoftware += atoi(cptr) * 100; + while (*cptr && isdigit(*cptr)) cptr++; + if (*cptr == '.') cptr++; + ServerSoftware += atoi(cptr); + return (ServerSoftware); +} + +/*****************************************************************************/ +/* +Using the WATCH [x]Script formatted debug output. +*/ + +void WatchErrorCallout () + +{ + char *type, *value; + PyObject *ptype, *pstype, *pvalue, *psvalue, *ptrace; + + PyErr_Fetch (&ptype, &pvalue, &ptrace); + pstype = PyObject_Repr (ptype); + psvalue = PyObject_Repr (pvalue); +#if PY_MAJOR_VERSION >= 3 + type = PyBytes_AsString (pstype); + value = PyBytes_AsString (psvalue); +#else + type = PyString_AsString (pstype); + value = PyString_AsString (psvalue); +#endif + WatchCallout ("ERROR %s %s", type, value); + PyErr_Restore (ptype, pvalue, ptrace); +} + +/*****************************************************************************/ +/* +Just a wrapper. +*/ + +char* TrnLnm (char *lnm) +{ + return (Trn2Lnm (lnm, NULL, 0)); +} + +/*****************************************************************************/ +/* +Translate a logical name using LNM$FILE_DEV by default or the specified name +table. Returns a pointer to the value string, or NULL if the name does not +exist. 'IndexValue' should be zero for a 'flat' logical name, or 0..127 for +interative translations. Returns pointer to non-reentrant static buffer which +must be strdup()ed if retained. +*/ + +char* Trn2Lnm ( char *LogName, @@ -4308,5 +4828,6 @@ ( char *LogName, -char *LogValue +char *LogTable, +int IndexValue ) { @@ -4311,5 +4832,7 @@ ) { - static unsigned short ShortLength; - static char StaticLogValue [256]; + static unsigned short ValueLength; + static unsigned long LnmAttributes, + LnmIndex; + static char LogValue [256]; static $DESCRIPTOR (LogNameDsc, ""); @@ -4315,5 +4838,5 @@ static $DESCRIPTOR (LogNameDsc, ""); - static $DESCRIPTOR (LnmFileDevDsc, "LNM$FILE_DEV"); + static $DESCRIPTOR (LogTableDsc, ""); static struct { short int buf_len; short int item; @@ -4321,8 +4844,10 @@ unsigned short *ret_len; } LnmItems [] = { - { 255, LNM$_STRING, 0, &ShortLength }, + { sizeof(LnmIndex), LNM$_INDEX, &LnmIndex, 0 }, + { sizeof(LnmAttributes), LNM$_ATTRIBUTES, &LnmAttributes, 0 }, + { sizeof(LogValue), LNM$_STRING, LogValue, &ValueLength }, { 0,0,0,0 } }; int status; @@ -4325,10 +4850,9 @@ { 0,0,0,0 } }; int status; - char *cptr; /*********/ /* begin */ /*********/ @@ -4330,9 +4854,20 @@ /*********/ /* begin */ /*********/ - if (dbug) fprintf (stdout, "TrnLnm() |%s|\n", LogName); + LnmIndex = IndexValue; + + if (LogTable) + { + LogTableDsc.dsc$a_pointer = LogTable; + LogTableDsc.dsc$w_length = strlen(LogTable); + } + else + { + LogTableDsc.dsc$a_pointer = "LNM$FILE_DEV"; + LogTableDsc.dsc$w_length = sizeof("LNM$FILE_DEV")-1; + } LogNameDsc.dsc$a_pointer = LogName; LogNameDsc.dsc$w_length = strlen(LogName); @@ -4336,12 +4871,7 @@ LogNameDsc.dsc$a_pointer = LogName; LogNameDsc.dsc$w_length = strlen(LogName); - if (LogValue) - cptr = LnmItems[0].buf_addr = LogValue; - else - cptr = LnmItems[0].buf_addr = StaticLogValue; - - status = sys$trnlnm (0, &LnmFileDevDsc, &LogNameDsc, 0, &LnmItems); - if (dbug) fprintf (stdout, "sys$trnlnm() %%X%08.08X\n", status); - if (!(status & 1)) + + status = sys$trnlnm (0, &LogTableDsc, &LogNameDsc, 0, &LnmItems); + if (!(status & 1) || !(LnmAttributes & LNM$M_EXISTS)) { @@ -4347,5 +4877,5 @@ { - if (dbug) fprintf (stdout, "|(null)|\n"); + if (LogValue) LogValue[0] = '\0'; return (NULL); } @@ -4349,9 +4879,8 @@ return (NULL); } - cptr[ShortLength] = '\0'; - if (dbug) fprintf (stdout, "|%s|\n", cptr); - return (cptr); + LogValue[ValueLength] = '\0'; + return (LogValue); } /*****************************************************************************/ diff --git a/src/python/readmore.html b/src/python/readmore.html index 9f21a62c1055b5b818c32c9850f57633a0ec207d_c3JjL3B5dGhvbi9yZWFkbW9yZS5odG1s..aff157d9025310d1eee56d29d81fd9d7ee58a922_c3JjL3B5dGhvbi9yZWFkbW9yZS5odG1s 100644 --- a/src/python/readmore.html +++ b/src/python/readmore.html @@ -88,5 +88,7 @@ <center> <h1>Python Run-Time Environment</h1> -<h3 style="margin-bottom:0;">Version 1.1.12, 30th July 2017</h3> +<h3>~~~ PRE-RELEASE ~~~</h3> +<h3>Version 2.0.0, 8th September 2020 +<br>Version 3.0.0, 8th September 2020</h3> @@ -92,5 +94,5 @@ -<p><b>Copyright © 2007-2017 Mark G. Daniel</b> +<p><b>Copyright © 2007-2020 Mark G. Daniel</b> <br>This program, comes with ABSOLUTELY NO WARRANTY. <br>This is free software, and you are welcome to redistribute it under the <br>conditions of the GNU GENERAL PUBLIC LICENSE, version 3, or any later version. @@ -106,6 +108,7 @@ <li><a href="#example">Example Scripts</a> <li><a href="#wsgi">Web Server Gateway Interface</a> <li><a href="#lever">Leveraging PyRTE</a> +<li><a href="#WATCHing">WATCHing Scripts</a> <li><a href="#problems">Problems</a> <li><a href="#releases">Releases</a> <li><a href="#ackn">Acknowlegements</a> @@ -128,10 +131,10 @@ command-line OpenVMS Python. Alpha (AXP) and Itanium object modules are provided for the VMS platforms available for Python. -<p> <span class="pyrte">PyRTE</span> has been developed and tested on VMS V8.3 -and V8.4, Alpha and Itanium, using WASD v10.<i>n</i> and v11.<i>n</i> and VMS -Python kits PYTHON272 and PYTHON278 available from and documented at +<p> <span class="pyrte">PyRTE</span> has been developed and tested on +V8.4, Alpha and Itanium, using WASD v11.<i>n</i> and VMS Python kits available +from and documented at <a target="_blank" href="https://www.vmspython.org/">https://www.vmspython.org/</a>. JFP's Python kits are kept very up-to-date as Python updates or implementation problems arise. @@ -134,7 +137,13 @@ <a target="_blank" href="https://www.vmspython.org/">https://www.vmspython.org/</a>. JFP's Python kits are kept very up-to-date as Python updates or implementation problems arise. +<p> <b>FYI:</b> Beginning August 2020, PyRTE underwent significant rework +prompted by the pre-release of JFP's Python v3.10 port. The core PyRTE code +now supports both Python 2.7 and 3.10 as independent builds and executables. +PyRTE reports itself as v2.<i>n</i>.<i>n</i> for the Python 2.7 build, and as +v3.<i>n</i>.<i>n</i> for Python 3.10. + <a name="install"> <h2>Installation</h2> </a> @@ -179,9 +188,13 @@ $ SET DEFAULT WASD_ROOT:[SRC.PYTHON] $ @BUILD_PYRTE LINK </pre> +For Python 3: +<pre class="code"> +$ @BUILD_PYRTE3 LINK +</pre> <li> Copy the <span class="pyrte">PyRTE</span> engine to the script executable location. <pre class="code"> $ COPY WASD_EXE:PYRTE.EXE CGI_EXE: </pre> @@ -182,9 +195,13 @@ <li> Copy the <span class="pyrte">PyRTE</span> engine to the script executable location. <pre class="code"> $ COPY WASD_EXE:PYRTE.EXE CGI_EXE: </pre> +For Python 3: +<pre class="code"> +$ COPY WASD_EXE:PYRTE3.EXE CGI_EXE: +</pre> <li> <a href="#configure_wasd">Configure</a> the Web server. @@ -188,7 +205,7 @@ <li> <a href="#configure_wasd">Configure</a> the Web server. -<p><li> Start scripting <tt>:^)</tt> +<p><li> Start scripting <span style="font-size:130%;">☺</span> <br>Try the <a href="#example">example scripts</a>. </ul> @@ -211,7 +228,7 @@ <ul> -<li> Server global configuration (HTTPD$CONFIG) can be used to indicate which -file types should activate the Python engine and how perform non-script access. +<li> Server global configuration can be used to indicate which file types +should activate the Python engine and how perform non-script access. <pre class="code"> @@ -216,6 +233,6 @@ <pre class="code"> -# HTTPD$CONFIG +# WASD_CONFIG_GLOBAL [DclScriptRunTime] .PY $CGI-BIN:[000000]PYRTE [AddType] @@ -224,7 +241,15 @@ .PYO application/octet-stream Python optimised byte-code </pre> +For Python 3: + +<pre class="code"> +# WASD_CONFIG_GLOBAL +[DclScriptRunTime] +.PY $CGI-BIN:[000000]PYRTE3 +</pre> + <li> The following rules require the Python script files to be located in the site administrator controlled /cgi-bin/ path. <pre class="code"> @@ -227,9 +252,9 @@ <li> The following rules require the Python script files to be located in the site administrator controlled /cgi-bin/ path. <pre class="code"> -# HTTPD$MAP +# WASD_CONFIG_MAP exec /py-bin/* (cgi_exe:pyrte)/cgi-bin/* \ script=syntax=unix script=query=none map=once ods=5 </pre> @@ -232,8 +257,16 @@ exec /py-bin/* (cgi_exe:pyrte)/cgi-bin/* \ script=syntax=unix script=query=none map=once ods=5 </pre> +For Python 3: + +<pre class="code"> +# WASD_CONFIG_MAP +exec /py-bin/* (cgi_exe:pyrte3)/cgi-bin/* \ + script=syntax=unix script=query=none map=once ods=5 +</pre> + <li> Alternatively, to automatically map scripts ending in .py to the RTE engine. <pre class="code"> @@ -236,10 +269,10 @@ <li> Alternatively, to automatically map scripts ending in .py to the RTE engine. <pre class="code"> -# HTTPD$MAP for automatic RTE usage +# WASD_CONFIG_MAP for automatic RTE usage map /cgi-bin/*.py* /py-bin/*.py* exec /py-bin/* (cgi_exe:pyrte)/cgi-bin/* \ script=syntax=unix script=query=none map=once ods=5 </pre> @@ -241,10 +274,17 @@ map /cgi-bin/*.py* /py-bin/*.py* exec /py-bin/* (cgi_exe:pyrte)/cgi-bin/* \ script=syntax=unix script=query=none map=once ods=5 </pre> +For Python 3: + +<pre class="code"> +exec /py-bin/* (cgi_exe:pyrte3)/cgi-bin/* \ + script=syntax=unix script=query=none map=once ods=5 +</pre> + <li> The following rule would allow .py type files anywhere in the mapped directory structure to be executed. This may be what is desired but might be dangerous in some circumstances. <pre class="code"> @@ -246,9 +286,9 @@ <li> The following rule would allow .py type files anywhere in the mapped directory structure to be executed. This may be what is desired but might be dangerous in some circumstances. <pre class="code"> -# HTTPD$MAP +# WASD_CONFIG_MAP exec /web/**.py* /web/*.py* \ script=syntax=unix script=query=none map=once ods=5 </pre> @@ -260,7 +300,7 @@ detail). <pre class="code"> -# HTTPD$MAP +# WASD_CONFIG_MAP exec /appx/**.py* /appx_root/*.py* \ script=syntax=unix script=query=none map=once ods=5 \ script=as=USERX @@ -270,8 +310,8 @@ following configuration. <pre class="code"> -# HTTPD$MAP +# WASD_CONFIG_MAP exec /py-bin/* (cgi_exe:pyrte)/wasd_root/src/python/scripts/* \ script=syntax=unix script=query=none map=once ods=5 </pre> @@ -274,7 +314,14 @@ exec /py-bin/* (cgi_exe:pyrte)/wasd_root/src/python/scripts/* \ script=syntax=unix script=query=none map=once ods=5 </pre> +For Python 3: + +<pre class="code"> +exec /py-bin/* (cgi_exe:pyrte3)/wasd_root/src/python/scripts/* \ + script=syntax=unix script=query=none map=once ods=5 +</pre> + Note that the location of the above example scripts has changed to [.SCRIPTS] since the v1.0 release. @@ -448,6 +495,51 @@ $ HTTPD/DO=DELETE </pre> +<a name="WATCHing"> +<h2>WATCHing Scripts</h2> +</a> + +<p> Debugging is always fun <span style="font-size:130%;">☹</span> +<p> PyRTE provides some execution data via the WATCH report [X]Script item. +For example (note the SCRIPTs): + +<pre class="code" style="width:100%;;padding-right:40%;"> +|00:30:50.08 SERVICE 1735 093001 CONNECT VIRTUAL klaatu.local:443| +|00:30:50.08 REQUEST 4318 093001 REQUEST GET /py-bin/wsgi_test3.py| +|00:30:50.09 CACHE 0604 093001 RESPONSE CACHE search path 5F1A99EFCB98C60126B8F244110E5BDD| +|00:30:50.09 DCL 1572 093001 RESPONSE SCRIPT as HTTP$NOBODY RTE /py-bin/wsgi_test3.py wasd_root:[src.python.scripts]wsgi_test3.py ($cgi_exe:pyrte.exe)| +|00:30:50.10 DCL 7962 125001 SCRIPT PYRTE AXP-2.0.0 Python 2.7.18 (default, Jul 23 2020, 17:40:05) [DECC]| +|00:30:50.10 DCL 7945 093001 SCRIPT RTE caching /py-bin/wsgi_test3.py /WASD_ROOT/src/PYTHON/SCRIPTS/wsgi_test3.py| +|00:30:50.10 DCL 7945 093001 SCRIPT CACHE new 1/1| +|00:30:50.10 DCL 7945 093001 SCRIPT LOAD /py-bin/wsgi_test3.py /WASD_ROOT/src/PYTHON/SCRIPTS/wsgi_test3.py| +|00:30:50.10 DCL 7945 093001 SCRIPT CODE /WASD_ROOT/src/PYTHON/SCRIPTS/wsgi_test3.py| +|00:30:50.11 DCL 7945 093001 SCRIPT EVAL /py-bin/wsgi_test3.py| +|00:30:50.12 DCL 7945 093001 SCRIPT PYRTE AXP-2.0.0 USAGE:1/19 REAL:00:00:00.02 CPU:0.02 DIO:3 BIO:36 FAULTS:0 PGFL:463040/8%| +|00:30:50.12 GZIP 0608 093001 RESPONSE DEFLATE 1289->507 bytes, 39% (261kB)| +|00:30:50.12 REQUEST 1438 093001 REQUEST STATUS 200 (OK) rx:78 tx:2415 bytes 1.045832 seconds 2,383 B/s| +</pre> + +<p> Any reportable script error is indicated. + +<pre class="code" style="width:100%;padding-right:20%;"> +|00:33:50.17 DCL 7962 265001 SCRIPT EVAL /py-bin/pyrte_test3.py| +|00:33:56.82 DCL 7962 265001 SCRIPT ERROR <type 'exceptions.RuntimeError'> 'logical name PYRTE_CACHE_ENTRY not defined'| +</pre> + +<p> It is also possible to add items from within the script Python code using +the wasd.WATCH() function. The example /py-bin/pyrte_test1.py script contains +the statement <tt>wasd.WATCH('and hello [x]Script')</tt> + +<pre class="code" style="width:100%;padding-right:25%;"> +|00:35:15.05 DCL 7945 057001 SCRIPT RTE caching /py-bin/pyrte_test1.py /WASD_ROOT/src/PYTHON/SCRIPTS/pyrte_test1.py| +|00:35:15.05 DCL 7945 057001 SCRIPT CACHE new 1/1| +|00:35:15.05 DCL 7945 057001 SCRIPT LOAD /py-bin/pyrte_test1.py /WASD_ROOT/src/PYTHON/SCRIPTS/pyrte_test1.py| +|00:35:15.05 DCL 7945 057001 SCRIPT CODE /WASD_ROOT/src/PYTHON/SCRIPTS/pyrte_test1.py| +|00:35:15.06 DCL 7945 057001 SCRIPT EVAL /py-bin/pyrte_test1.py| +|00:35:15.07 DCL 7945 057001 SCRIPT WATCH and hello [x]Script| +|00:35:15.07 DCL 7945 057001 SCRIPT PYRTE AXP-2.0.0 USAGE:1/1 REAL:00:00:05.10 CPU:5.06 DIO:65 BIO:1060 FAULTS:870 PGFL:476448/5%| +</pre> + <a name="problems"> <h2>Problems?</h2> </a> @@ -481,8 +573,17 @@ <dl> +<dt>v3.0.0 08-SEP-2020 ~~~ PRE-RELEASE ~~~</dt> +<dd>• Python version 3.10.0a0</dd> +</dd> + +<dt>v2.0.0 08-SEP-2020 ~~~ PRE-RELEASE ~~~</dt> +<dd>• Python version 2.7.18</dd> +</dd> + +</dd> <dt>v1.1.12 30-JUL-2017</dt> <dd>• wasd.cgi_callout() <dd>• bugfix </dd> @@ -484,56 +585,9 @@ <dt>v1.1.12 30-JUL-2017</dt> <dd>• wasd.cgi_callout() <dd>• bugfix </dd> -<dt>v1.1.11 20-JUN-2013</dt> -<dd>• bugfix -</dd> - -<dt>v1.1.10 05-NOV-2011</dt> -<dd>• Python 2.7.2, see changes to includes -</dd> - -<dt>v1.1.9 27-FEB-2011</dt> -<dd>• /CLI= to allow a command-line script activation -<dd>• logical PYRTE_METRICS enables "?$metrics$" report -</dd> - -<dt>v1.1.8 20-JUL-2010</dt> -<dd>• WASD v10.1 proctor detect -</dd> - -<dt>v1.1.7 17-MAY-2010</dt> -<dd>• bugfix -</dd> - -<dt>v1.1.6 30-APR-2010</dt> -<dd>• bugfix -</dd> - -<dt>v1.1.5 29-APR-2010</dt> -<dd>• PYRTE_HEAD_CVT_GET and script=param=PYRTE=/HEAD=GET -</dd> - -<dt>v1.1.4 08-DEC-2009</dt> -<dd>• bugfix -</dd> - -<dt>v1.1.3 07-DEC-2009</dt> -<dd>• bugfix -</dd> - -<dt>v1.1.2 19-APR-2008</dt> -<dd>• bugfix -</dd> - -<dt>v1.1.1 17-JAN-2008</dt> -<dd>• bugfix -</dd> - -<dt>v1.1.0 05-JAN-2008</dt> -<dd>• WSGI support</dd> -</dd> +⋮ <dt>v1.0.0 22-APR-2007</dt> <dd>• initial</dd>