/*****************************************************************************/ #ifdef COMMENTS_WITH_COMMENTS /* aLaMode.c Browser based HTTPDMON-like (on steroids) WASD monitoring application. WASD has produced a Spanish sounding application (MonDeSi) so why not another with a French flavour? Although rather than meaning "fashionable" (cannot be said of VMS or WASD), this application's name is intended to be reminscent of the North American usage, "on the top or side". Not being much of a dessert person I might even prefer the cookery usage of "meats braised with vegetables in wine" but that's too much of a stretch :-) Whatever the force-fit it seemed less prosaic than WASDmon. This code is the data-gathering back-end. The front-end is JavaScript driven. The front-end connects via WebSocket to this script and it provides JSON formatted data back to the front-end for processing and display. WASD v11.0 brought a change of thinking around versioning. Although many releases will not be as dramatic as the move from v10 to v11, aLaMode is so closely intergrated with the server there is some merit in having the versioning more obviously related. Now "major" and "minor" values will be synced with server version and the "tweak" value an aLaMode alphabetic bugfix indicator. Major versions (e.g. v10 to v11) will always be bumped but if a minor version change is aLaMode backward-compatible then the minor value can stay at the previous value (and a specific aLaMode release not be needed). As this application does not lock the global section when reading values there is the possibility of inconsistent resultant data. Atomic reads are OK (e.g. a longword or quadword (on non-VAX). Data with relationships (i.e. peak processing and current processing count) are an example because of the processing occuring between the individual reads. Another is the URI of the latest request data (read character by character and therefore subject to overwriting during the copy). Rather than introduce the expense of a lock to mitigate the risk, for an observational application like this it is considered an acceptable tradeoff. This application accesses the WASD global lock environment used to coordinate and control WASD instances within a single system and across a cluster. As a consequnce of participating in the control lock environment directives sent to the WASD envionment will report additional "instances" being notified. $ httpd/do=map %HTTPD-I-DO, 2 instances notified; KLAATU::WASD:80-70, KLAATU::WASD:80 The "instances" relating to alamode and those to servers should easily be differentiated by process name. The application is intended to communicate browser<->script using WebSocket but can be used CGI employing XHR (XMLHttpRequest()) and a long-poll CGI response. Multiple JSON structures can be incorporated into (a single write and) a single response stream by delimitting each with a sentinal sequence that cannot occur naturally in a legal JSON structure. This allows individual JSON structures to be parsed from the stream. The sequence """ (three consecutive quotation characters) is used as the sentinal. The same technique is used for long-poll CGI-responses, and in WebSocket writes (to reduce the number of individual I/Os). aLaMode may be proctored into existence. # WASD_CONFIG_GLOBAL [DclScriptProctor] 1 /cgiplus-bin/alamode /cgiplus-bin/alamode MONITOR DISPLAY --------------- A node is displayed as a text panel on the left and bar graphs on the right. These display the instantaneous (current) values for the various resources and are updated dynamically at the interval when the data collector script detects a change in values. Each bar graph section contains a number representing the current value and a number in the far right the maximum value reached while monitoring. All values are average per-second where applicable. Important element of the display have assocaited exaplanatory tooltips. AUTHORISATION ------------- This application provides substantial insight (at some level) into a site's Web server profile and activity, as well as to the detail of some requests. The application insists on authorised access and unless this is configured will not permit access. This can be a simple rule providing unrestricted access (where the username becomes "WORLD") [WORLD] /cgi*-bin/alamode* read,https: If the username is WORLD then authenticated requests have the username and request URI display obfuscated (i.e. *********** out). or when authentication is actually exercised ["A La Mode"=VMS] /cgi*-bin/alamode* read,https: or further constrained by specified users ["A La Mode"=VMS] /cgi*-bin/alamode* ~USER1,~USER2,read,https: CONFIGURATION LOGICALS ---------------------- Logical names defined /SYSTEM or in a table accessable to the scripting process. ALAMODE_ALERT_403 integer HTTP 403 status per minute before alert ALAMODE_ALERT_5NN integer HTTP 500,501... status per minute before alert ALAMODE_ALERT_NNN =[,...] (e.g. "401=10,407=15") ALAMODE_GEOLOCATE "*" to enable default geolocation, or prefered service ALAMODE_NI network devices to be monitored (e.g. EWA1:,EWA2:) ALAMODE_QUERY defines default query string configuration PRIVILEGES ---------- SHARE if other utilities (e.g. MONDESI) with channels allocated to NI SYSPRV access to global section (WASD accounting data) SYSLCK access WASD lock data (system/cluster IPC and coordination) WORLD access server process information (non-script account) $ INSTALL ADD CGI-BIN:[000000]ALAMODE.EXE /PRIVILEGE=(SHARE,SYSPRV,SYSLCK,WORLD) COPYRIGHT --------- Copyright (C) 2014-2022 Mark G.Daniel This program comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under the conditions of the GNU GENERAL PUBLIC LICENSE, version 3, or any later version. http://www.gnu.org/licenses/gpl.txt VERSION HISTORY --------------- 22-APR-2022 MGD v12.0.b, geolocation now via GEOLOCATE.C bugfix; JsonStaticData() Size64 = (uint)... bugfix; various ..64 required ..64[HTTP12] 24-MAR-2021 MGD v12.0.a, VAX no longer implemented move to native 64 bit data storage proxy cache obsolete proxy rework and SOCKS5 introduced 07-MAR-2020 MGD v11.3.d, re-map global section when startup detected 20-JUL-2019 MGD v11.3.c, "One small step ..." bugfix; WebSockAvailable() 03-DEC-2018 MGD v11.3.b, bugfix; ALAMODE_GEOLOCATE and ALAMODE_QUERY make SysTrnLnm2() static storage dynamic 03-NOV-2018 MGD v11.3.a, WASD v11.3.0 ALAMODE_QUERY logical name 03-MAR-2018 MGD v11.2.a, WASD v11.2.0 add (instance) status report 11-AUG-2017 MGD v11.1.c, ALAMODE_GEOLOCATE client geolocation 29-JUL-2017 MGD v11.1.b, ALAMODE_ALERT_NNN alerts for user specified codes 06-MAY-2017 MGD v11.1.a, WASD v11.1.0 05-APR-2016 MGD v11.0.a, WASD v11.0 and HTTP/2 Change of thinking around version. See above. 09-SEP-2015 MGD v1.1.1, ReportInstanceControl() allow for transient SS$_VALNOTVALID DLM status bugfix; ReportInstanceControl() SS$_XVALNOTVALID 15-AUG-2015 MGD v1.1.0, add instance active/passive and connect suspend simplify JsonAlert() using vax_vsnprintf() 20-APR-2015 MGD v1.0.2, bugfix; ensure JsonAlert()s do not occur prematurely bugfix; ReportInstanceControl() 64 byte lock value 11-APR-2015 MGD v1.0.1, add BUFIO and DIRIO to instance data sys$getuai() to establish initial BYTLM (per JPP) bugfix; proxy delta data into CollectAccounting() bugfix; sys$gettim_prec defined IA64 V8.4 (per JPP) Happy 62nd Wedding Anniversary Mum and Dad 29-MAR-2015 MGD v1.0.0, initial release 01-FEB-2014 MGD initial development */ #endif /* COMMENTS_WITH_COMMENTS */ /*****************************************************************************/ #define SOFTWAREVN "12.0.b" /* ^^^^^^ don't forget to update ALAMODE.JS compliance and BUILD_ALAMODE.COM link identification! */ #define SOFTWARENM "ALAMODE" #define SOFTWARECR "Copyright (C) 2014-2022 Mark G.Daniel" #ifdef __ALPHA # define SOFTWAREID SOFTWARENM " AXP-" SOFTWAREVN #endif #ifdef __ia64 # define SOFTWAREID SOFTWARENM " IA64-" SOFTWAREVN #endif #ifdef __VAX # error VAX no longer implemented #endif #ifdef __x86_64 # define SOFTWAREID SOFTWARENM " X86-" SOFTWAREVN #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* require this for TCP/IP Services IPv6 */ #define _SOCKADDR_LEN /* BUT MultiNet BG driver does not support BSD 4.4 AF_INET addresses */ #define NO_SOCKADDR_LEN_4 #define TCPIP_LOOKUP_HOST_NAME_RETRY 3 /* at one second intervals */ #include #include #include #include #include "wslib.h" #ifdef INCLUDE_WASD_H #include INCLUDE_WASD_H #else #include "../httpd/wasd.h" #endif #ifndef ALAMODE_GEOLOCATE #define ALAMODE_GEOLOCATE #endif #ifdef ALAMODE_GEOLOCATE #include "geolocate.h" int Debug; #endif /* nomenclature rethink post 10.4.0 */ #ifdef WASDMON_SUPPORT_1040 #define ALAMODE_SUPPORT_1040 #endif #pragma message save #pragma message enable ALL #ifdef ALAMODE_SUPPORT_1200 # pragma message ("building for WASD v12.0.n") # define BUILDING_FOR "12.0.n" #else #error only for WASD v12.0.n and later #endif #ifndef BUILDING_FOR # error "unsupported WASD version (minimum build is v10.4.0)" #endif #pragma message restore /* logical names */ #define LOGNAM_ALERT_403 "ALAMODE_ALERT_403" #define LOGNAM_ALERT_5NN "ALAMODE_ALERT_5NN" #define LOGNAM_ALERT_NNN "ALAMODE_ALERT_NNN" #define LOGNAM_GEOLOCATE "ALAMODE_GEOLOCATE" #define LOGNAM_LIFETIME "ALAMODE_LIFETIME" #define LOGNAM_NI "ALAMODE_NI" #define LOGNAM_QUERY "ALAMODE_QUERY" #define LOGNAM_WEBSOCKET "ALAMODE_WEBSOCKET" #ifndef UINT64PTR /* mainly to allow easy use of the __unaligned directive */ #define INTPTR __unaligned int* #define INT64PTR __unaligned int64* #define LONGPTR __unaligned long* #define SHORTPTR __unaligned short* #define UINTPTR __unaligned unsigned int* #define ULONGPTR __unaligned unsigned long* #define USHORTPTR __unaligned unsigned short* #define UINT64PTR __unaligned uint64* #endif #define VMSok(x) ((x) & STS$M_SUCCESS) #define VMSnok(x) !(((x) & STS$M_SUCCESS)) #define FI_LI "ALAMODE", __LINE__ #define EXIT_FI_LI(status) \ { printf ("Status:500\r\n\r\n[%s:%d]\r\n", FI_LI); exit(status); } /* effectively disable WsLib's own timeout mechanism */ #define ALONGTIME 365*24*60*60 /* one year in seconds */ /* maximum number of network devices */ #define NI_DEVICE_MAX 8 /* array of status code values from ALAMODE_ALERT_NNN */ #define ALERT_ARRAY_MAX 20 /* puts three successive quotations into the stream - impossible JSON data */ #define JSON_SENTINAL "\"\"\"" /* number of these status codes per minute */ #define ALERT_STATUS_403 15 #define ALERT_STATUS_5NN 30 #define REFRESH_INTERVAL 2 /* seconds */ #define REFRESH_ALERT 5 /* alert when five times longer than should be */ /* exit after fifteen minutes without a WebSocket connection */ #define EXIT_AFTER_SECONDS 15 * 60; int AlertStatusCodeCount, AlertStatusCode403, AlertStatusCodeGroup5, ConnectedCount, DataViaCGI, dbug, InstanceEnvNumber = 1, /* default group number */ IsCgiPlus, NodesAcross, ProctoredScript = -1, RefreshInterval = REFRESH_INTERVAL, UsageCount, ViewWidth, WorldAccess; ulong ProvideDataSeconds, ExitTimeStamp, HttpdPid, EfnSpi, EfnWait, TimeStamp; int64 CollectTime64, CurrentTime64, ProvideTime64, StartTime64; ushort CurrentTime7 [7]; long NeedPrivMask [2] = { PRV$M_SHARE | PRV$M_SYSLCK | PRV$M_SYSPRV | PRV$M_WORLD, 0 }; float DeltaSeconds = 999999.0; char *CgiHttpRefererPtr, *CgiHttpUserAgentPtr, *CgiRemoteAddrPtr, *CgiRemoteUserPtr, *CgiRequestUriPtr, *CgiScriptNamePtr, *CgiQueryStringPtr, *CgiPlusEofPtr = NULL, *CgiPlusEotPtr = NULL, *CgiPlusEscPtr = NULL, *GeoLocatePtr; char NetIntDev [NI_DEVICE_MAX*24], RefererHost [128]; /* sys$gettim_prec() arrived with IA64 V8.4 */ #if defined(sys$gettim_prec) /* may also produce "%LINK-I-UDFSYM, GETTIM_PREC" which can safely be ignored */ int (*GetTimFunction)() = sys$gettim_prec; #else int (*GetTimFunction)() = sys$gettim; #endif /*******************/ /* data structures */ /*******************/ typedef struct STRUCT_IOSBLK { ushort iosb$w_status; ushort iosb$w_bcnt; ulong iosb$l_reserved; } IOSBLK; typedef struct STRUCT_STATUS_CODE { ulong code, current, previous, trigger; } ALERT_STATUS_CODE; #define IMAG_NAME_SIZE 127 #define UIC_STRING_MAX 63 #define JPI_USER_NAME_MAX 12 #define JPI_PRCNAM_MAX 15 typedef struct STRUCT_PROCESS { ulong JpiCpuId, JpiPrib, JpiPri, JpiWsExtent, JpiPpgCnt, JpiGpgCnt, JpiWsPeak, JpiPagFilCnt, JpiAstCnt, JpiAstLm, JpiBioCnt, JpiBioLm, JpiDioCnt, JpiDioLm, JpiEnqCnt, JpiEnqLm, JpiWsQuota, JpiPgFlQuota, JpiVirtPeak64, JpiFilLm, JpiFilCnt, JpiBytLm, JpiBytCnt, JpiPrcLm, JpiPrcCnt, JpiTqLm, JpiTqCnt, JpiMultiThread, JpiKtCount, JpiPageFlts, JpiBufIo, JpiDirIo, JpiVolumes, PageFaults, ProcessId, PrevPageFaults; int64 ConTime64, CpuTime64; char *ProcessState; char AstFlags [16], JpiPrcNam[JPI_PRCNAM_MAX+1]; } PROCESS_DATA; typedef struct STRUCT_CLIENT_DATA { BOOL Initialised; ulong ClientCount, ProvideDetailData, ProvideInstanceData, ProvideInstanceStatus, ProvideProxyData, ProvideRequestData, ProvideScriptData, ProvideWebDavData, WorldAccess; char InputBuffer [256], OutputBuffer [4096]; struct WsLibStruct *WsLibPtr; } CLIENT_DATA; typedef struct STRUCT_SYSTEM_DATA { int ConnectSuspend, DetailBufferLength, InstanceClusterCount, InstanceBufferLength, InstanceNodeCount, InstancePassive, InstanceStartupCount, LkiValBlkSize, PrevMinute, PrevInstanceStartupCount, ProxyBufferLength, RequestBufferLength, ScriptBufferLength, StaticBufferLength, StatusBufferLength, RuntimeBufferLength, SummaryBufferLength, SystemDelta, UsageCount, VmsVersionInteger, WebDavBufferLength; ulong ActiveCpuCount, AvailCpuCount, BufferedIO, CpuBusyPercent, CpuUserPercent, CpuTicksBusy, CpuTicksUser, ClusterMember, ClusterNodes, DirectIO, InstanceNextPid, InstanceNodeSuperPid, JpiPid, FcpRead, FcpWrite, MemInUse, MemInUseMax, MemSize, PageSize, ProcessCount; ulong BlockReadSize, BlockWriteSize, BytesPerSecAve, BytesPerSecMax, BytesPerSecMin, BytesRawPerSec, CurrentConnected, CurrentHttp1Connected, CurrentHttp2Connected, ConnectPeak, ConnectHttp1Peak, ConnectHttp2Peak, ConnectHttp1Total, ConnectHttp2Total, CurrentProcessing, CurrentHttp1Processing, CurrentHttp2Processing, CurrentScript, CurrentScriptCGIplus, CurrentScriptRTE, ConnectTotal, CurrentThrottled, CurrentThrottleBusyMetric, CurrentThrottleProcessing, CurrentThrottleQueued, CurrentWebSockets, DclProcessCurrent, CurrentDECnetCGI, CurrentDECnetOSU, CurrentDECnetTasks, MethodConnect, MethodCopy, MethodDelete, MethodGet, MethodHead, MethodLock, MethodMkCol, MethodMove, MethodOptions, MethodPost, MethodPropFind, MethodPropPatch, MethodPut, MethodSsh, MethodTrace, MethodUnLock, NetBytesPerSec, NetBytesRxPerSec, NetBytesTxPerSec, NetDatagramRx, NetDatagramTx, NetReadErr, NetWriteErr, PrevStatusCode403, PrevStatusCodeGroup5, ProcessingPeak, ProcessingHttp1Peak, ProcessingHttp2Peak, ProcessingTotal, ProcessingHttp1Total, ProcessingHttp2Total, SslCount, StatusCode403, TooBusy; ulong DeltaDclCrePrc, DeltaDclDelPrc, PrevTotalDclHardLimit, TotalDclCrePrc, TotalDclCrePrcDetach, TotalDclCrePrcPersona, TotalDclCrePrcPersonaDefault, TotalDclCrePrcPersonaInvUser, TotalDclCrePrcPersonaPrvUser, TotalDclDelPrc, TotalDclForceX, TotalDclHardLimit, TotalDclProctor; ulong DeltaAllScript, DeltaCgi, DeltaCgiPlus, DeltaCgiPlusReused, DeltaDECnet, DeltaDECnetReused, DeltaDECnetCgi, DeltaDECnetCgiReused, DeltaDECnetOsu, DeltaDECnetOsuReused, DeltaFile, DeltaHttp, DeltaHttp1, DeltaHttp2, DeltaOther, DeltaProxy, DeltaRequest, DeltaRequestFail, DeltaRte, DeltaRteReused, DeltaScriptCgi, DeltaThrottled, DeltaThrottleBusy, DeltaThrottleQueued, DeltaWebDavRead, DeltaWebDavTotal, DeltaWebDavWrite; ulong TotalAdmin, TotalAllOther, TotalAllScript, TotalAutoScript, TotalCgi, TotalCgiPlus, TotalCgiPlusReused, TotalConnected, TotalHttp2Connected, TotalDclCommand, TotalDECnet, TotalDECnetCgi, TotalDECnetCgiReused, TotalDECnetReused, TotalDECnetOsu, TotalDECnetOsuReused, TotalDclScript, TotalDir, TotalFile, TotalIsMap, TotalMenu, TotalNoModule, TotalProxy, TotalPut, TotalRte, TotalRteReused, TotalSsi, TotalThrottled, TotalThrottleBusy, TotalThrottleQueued, TotalUpdate, TotalWebDav, TotalWebDavLock, TotalWebDavOther, TotalWebDavRead, TotalWebDavWrite, TotalWebSocket; ulong ProxyDeltaNetwork, ProxyDeltaTotal, ProxyEnabled, ProxyLookupCache, ProxyLookupDNS, ProxyLookupError, ProxyLookupLiteral, ProxyNetwork, ProxyReworkCount, ProxyReworkNoType, ProxyReworkReplaceCount, ProxyReworkReplaceSearch, ProxyReworkTooBig, ProxySocks5Count, ProxySocks5Fail, ProxySocks5Success, ProxyTotal, ProxyTunnel, ProxyTunnelCurrent; ulong WebDavAgentAppleCount, WebDavAgentBsdCount, WebDavAgentMicrosoftCount, WebDavAgentLinuxCount, WebDavAgentOtherCount, WebDavAgentSunCount, WebDavEnabled, WebDavLockingEnabled, WebDavMetaReadAttemptCount, WebDavMetaReadCount, WebDavMetaWriteAttemptCount, WebDavMetaWriteCount, WebDavQuotaEnabled, WebDavRequestCount, WebDavXmlParseCount; ulong InstanceNodePid [INSTANCE_MAX], StatusCodeGroup [6]; int64 BootTime64, BlocksRawRx64, BlocksRawTx64, BytesRawRx64, BytesRawTx64, NetIntBlocksRx64, NetIntBlocksTx64, NetIntBlocksRxTx64, NetIntBytesRx64, NetIntBytesTx64, NetIntBytesRxTx64, NetIntErrorsHard64, NetIntErrorsSoft64, ProxyBlocksRawRx64, ProxyBlocksRawTx64, ProxyBytesRawRx64, ProxyBytesRawTx64, ProxyBytesRawRxTx64, ProxyBytesRxTx64, UpTime64, WebDavBytesRawRx64, WebDavBytesRawTx64; char *AlamodeQueryPtr; ALERT_STATUS_CODE AlertStatusCode [ALERT_ARRAY_MAX]; char aLaModePrcNam [15+1], ArchName [15+1], DetailBuffer [2048], HttpdVersion [15+1], HwName [60+1], InstanceBuffer [4096], NodeName [15+1], ProxyBuffer [2048], RequestBuffer [2048], ScriptBuffer [2048], StaticBuffer [2048], StatusBuffer [128*8*8], RuntimeBuffer [256], SummaryBuffer [2048], VmsVersion [8+1], WebDavBuffer [2048]; } SYSTEM_DATA; CLIENT_DATA CgiClientData; SYSTEM_DATA SystemData; int HttpdGblSecLength; HTTPD_GBLSEC *gblptr, *HttpdGblSecPtr; ACCOUNTING_STRUCT *accptr; PROXY_ACCOUNTING_STRUCT *pacptr; #define INETACP$C_TRANS 2 #define INETACP_FUNC$C_GETHOSTBYNAME 1 #define INETACP_FUNC$C_GETHOSTBYADDR 2 $DESCRIPTOR (TcpIpDeviceDsc, "UCX$DEVICE"); /***********************/ /* function prototypes */ /***********************/ void AddClient (); int CheckHttpdPid (void); void CollectAccounting (); void CollectInstanceData (); void CollectNetIntData (); int CollectProcessData (ulong, PROCESS_DATA*); void CollectProxyData (); void CollectScriptData (); int CollectStaticData (); int CollectSPI (); void CollectWebDavData (); int HavePriv (ulong*); void JsonAlert (CLIENT_DATA*, char*, ...); void JsonDo (CLIENT_DATA*, char*, ...); void JsonDetailData (); void JsonInstanceData (); void JsonInstanceStatus (BOOL); void JsonRequestData (CLIENT_DATA*, BOOL); void JsonRuntime (); void JsonScriptData (); void JsonSummaryData (); void JsonWebDavData (); void MapGlobalSection (void); int MinimumWASD (char*); char* MungeTime (int64*); void ProvideData (); void ReadClient (struct WsLibStruct*); void RemoveClient (struct WsLibStruct*); void ReportInstanceControl (struct lksb*); void SetInterval (void); /* number 2 to avoid a clash in wasd.h */ char* SysTrnLnm2 (char*, char*, int); BOOL TcpIpIsAddress (char*); char* TcpIpLookup (char*, char*, uchar*, uchar*); void ThisLongAgo (int64*, char*); void TimeSansYear (ulong*, char*); int WebSockAvailable (); void XhrResponse (); /*****************************************************************************/ /* AST delivery is disabled during client acceptance and the add-client function is deferred using an AST to help minimise the client setup window with a potentially busy WebSocket application. */ main (int argc, char *argv[]) { int status; char *cptr, *sptr, *zptr; char aLaModePrcNam [15+1]; $DESCRIPTOR (PrcNamDsc, "aLaMode"); /*********/ /* begin */ /*********/ if (argc > 1) { if (!strcasecmp (argv[1], "/VERSION")) { fprintf (stdout, "%%ALAMODE-I-VERSION, %s %s\n", SOFTWAREID, WsLibVersion()); exit (SS$_NORMAL); } if (!strcasecmp (argv[1], "/DBUG")) dbug = true; } /* don't want the C-RTL fiddling with the carriage control */ if (!dbug) stdout = freopen ("SYS$OUTPUT", "w", stdout, "ctx=bin"); if (!HavePriv (NeedPrivMask)) EXIT_FI_LI (SS$_NOPRIV); #if !defined(__ALPHA) /* cater for "%LINK-I-UDFSYM, GETTIM_PREC" on unsupported platform */ if (GetTimFunction == NULL) GetTimFunction = sys$gettim; #endif GetTimFunction (&StartTime64); WsLibInit (); /* no clients is fifteen minutes in seconds */ WsLibSetLifeSecs (15*60); if (VMSnok (status = lib$get_ef (&EfnWait))) EXIT_FI_LI (status); if (VMSnok (status = lib$get_ef (&EfnSpi))) EXIT_FI_LI (status); if (cptr = SysTrnLnm2 (LOGNAM_ALERT_403, NULL, 0)) AlertStatusCode403 = atoi(cptr); if (!AlertStatusCode403) AlertStatusCode403 = ALERT_STATUS_403; if (cptr = SysTrnLnm2 (LOGNAM_ALERT_5NN, NULL, 0)) AlertStatusCodeGroup5 = atoi(cptr); if (!AlertStatusCodeGroup5) AlertStatusCodeGroup5 = ALERT_STATUS_5NN; if (cptr = SysTrnLnm2 (LOGNAM_ALERT_NNN, NULL, 0)) { /* =[,...] (e.g. "401=10,407=15") */ while (*cptr) { while (*cptr && !isdigit(*cptr)) cptr++; if (!*cptr) break; SystemData.AlertStatusCode[AlertStatusCodeCount].code = atoi(cptr); while (*cptr && isdigit(*cptr)) cptr++; while (*cptr && !isdigit(*cptr)) cptr++; SystemData.AlertStatusCode[AlertStatusCodeCount].trigger = atoi(cptr); while (*cptr && isdigit(*cptr)) cptr++; if (++AlertStatusCodeCount >= ALERT_ARRAY_MAX) break; } } if (cptr = SysTrnLnm2 (LOGNAM_GEOLOCATE, NULL, 0)) { strcpy (GeoLocatePtr = malloc (strlen(cptr)+1), cptr); /* convert previous JavaScript URL to default GEOLOCATE.C */ if (strstr (GeoLocatePtr, ".js")) GeoLocatePtr = "*"; } if (cptr = SysTrnLnm2 (LOGNAM_QUERY, NULL, 0)) strcpy (SystemData.AlamodeQueryPtr = malloc (strlen(cptr)+1), cptr); MapGlobalSection (); CollectStaticData (); if (SystemData.aLaModePrcNam[0] != '/') { /* not v12.0 or later process naming */ sprintf (aLaModePrcNam, "aLaMode_%4.4X", SystemData.JpiPid & 0xffff); PrcNamDsc.dsc$a_pointer = aLaModePrcNam; PrcNamDsc.dsc$w_length = strlen(aLaModePrcNam); if (!(status = sys$setprn (&PrcNamDsc) & 1)) EXIT_FI_LI (status); } CollectInstanceData (); ReportInstanceControl (NULL); JsonStaticData (); ProvideData (); if (dbug) for(;;) sys$hiber(); IsCgiPlus = WsLibIsCgiPlus(); for (;;) { /* with CGIplus this call will block waiting for the next request */ WsLibCgiVar (""); Setdbug (1); if (ProctoredScript < 0) { /* check for proctor instantiation */ ProctoredScript = !*WsLibCgiVar ("REMOTE_ADDR") && !*WsLibCgiVar ("SERVER_ADDR"); if (ProctoredScript) { ScriptCallout ("!LIFETIME: DO-NOT-DISTURB\n"); fprintf (stdout, "Status: 204\n\n"); WsLibCgiPlusEof (); continue; } } /* start by disabling the WsLib..() idle timeout */ WsLibSetLifeSecs (ALONGTIME); if ((cptr = SysTrnLnm2 (LOGNAM_LIFETIME, NULL, 0)) != NULL) { /* the op wants me to live this long (non-integer means DND) */ ScriptCallout ("!LIFETIME: DO-NOT-DISTURB\n"); if (isdigit(*cptr)) { /* this many minutes after the most recent request */ WsLibSetLifeSecs ((atol(cptr)*60)+1); } } /********************/ /* request received */ /********************/ UsageCount++; if (!MinimumWASD ("10.4.0")) { fprintf (stdout, "Status: 500\r\n\r\nMinimum WASD v10.4.0!\n"); exit (SS$_NORMAL); } CgiHttpRefererPtr = WsLibCgiVar("HTTP_REFERER"); CgiHttpUserAgentPtr = WsLibCgiVar("HTTP_USER_AGENT"); CgiQueryStringPtr = WsLibCgiVar("QUERY_STRING"); CgiRemoteAddrPtr = WsLibCgiVar("REMOTE_ADDR"); CgiRemoteUserPtr = WsLibCgiVar("REMOTE_USER"); CgiRequestUriPtr = WsLibCgiVar("REQUEST_URI"); CgiScriptNamePtr = WsLibCgiVar("SCRIPT_NAME"); /* derive the URL scheme and host from the HTTP referrer field */ zptr = (sptr = RefererHost) + sizeof(RefererHost)-1; cptr = CgiHttpRefererPtr; while (*cptr && *cptr != '/' && sptr < zptr) *sptr++ = *cptr++; if (*cptr == '/' && sptr < zptr) *sptr++ = *cptr++; if (*cptr == '/' && sptr < zptr) *sptr++ = *cptr++; while (*cptr && *cptr != '/' && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; if (!CgiRemoteUserPtr[0]) fprintf (stdout, "Status: 500\r\n\r\nUnauthorised!\n"); else if (MATCH6(CgiQueryStringPtr,"data=1")) { /********/ /* data */ /********/ if (DataViaCGI = !WsLibCgiVarNull ("WEBSOCKET_INPUT")) { if (!strcmp (CgiRemoteUserPtr, "WORLD")) WorldAccess = true; else WorldAccess = false; XhrResponse (); /* ProvideData() now drives it and should never ... */ for(;;) sys$hiber(); } if (!WebSockAvailable()) { fprintf (stdout, "Status: 500\r\n\r\nWebSocket not available!\n"); exit (SS$_NORMAL); } if (!IsCgiPlus) { fprintf (stdout, "Status: 500\r\n\r\nMust be CGIplus!\n"); exit (SS$_NORMAL); } AddClient (); } else { /********/ /* page */ /********/ if (WebSockAvailable()) { if (!IsCgiPlus) { if (!strncasecmp (cptr = CgiRequestUriPtr, "/cgi-bin/", 9)) { /* redirect to "standard" CGIplus path */ fprintf (stdout, "Status: 302\r\n\ Location: /cgiplus-bin/%s\r\n\ \r\n\ Must be CGIplus!\n", cptr+9); } else { /* not the "standard" path */ fprintf (stdout, "Status: 500\r\n\r\nMust be CGIplus!\n"); } exit (SS$_NORMAL); } } fprintf (stdout, "Content-Type: text/html\r\n\ \r\n\ \n\ \n\ \n\ àlamode\n\ \n\ \n\ \n\ \n\ \n\ \n\ àlamode v%s \n\ \n\ \n", SOFTWARECR, SOFTWAREVN, WsLibCgiVar("SCRIPT_NAME"), WebSockAvailable() && IsCgiPlus ? "true" : "false", SOFTWAREVN); } Setdbug (0); if (!IsCgiPlus) break; memset (&CgiClientData, 0, sizeof(CgiClientData)); WsLibCgiPlusEof (); } exit (SS$_NORMAL); } /*****************************************************************************/ /* Allocate a client structure and add it to the head of the list. Establish the WebSocket IPC and begin processing. */ void AddClient () { int status; CLIENT_DATA *clptr; /*********/ /* begin */ /*********/ if (!(clptr = calloc (1, sizeof(CLIENT_DATA)))) EXIT_FI_LI (vaxc$errno); /* create a WebSocket library structure for the client */ if (!(clptr->WsLibPtr = WsLibCreate (clptr, RemoveClient))) { /* failed, commonly on some WebSocket protocol issue */ return; } /* open the IPC to the WebSocket (mailboxes) */ status = WsLibOpen (clptr->WsLibPtr); if (VMSnok (status)) EXIT_FI_LI (status); if (!strcmp (CgiRemoteUserPtr, "WORLD")) clptr->WorldAccess = true; WsLibWatchScript (clptr->WsLibPtr, FI_LI, "!AZ", SOFTWAREID); /* provide the static system data */ WsLibWrite (clptr->WsLibPtr, SystemData.StaticBuffer, SystemData.StaticBufferLength, WSLIB_ASYNCH); clptr->Initialised = TRUE; /* queue an asynchronous read from the client */ WsLibRead (clptr->WsLibPtr, clptr->InputBuffer, sizeof(clptr->InputBuffer)-1, ReadClient); ConnectedCount++; } /*****************************************************************************/ /* Remove the client structure from the list and free the memory. */ void RemoveClient (struct WsLibStruct *wsptr) { struct MonClient *clptr; /*********/ /* begin */ /*********/ clptr = WsLibGetUserData(wsptr); if (ConnectedCount) ConnectedCount--; } /*****************************************************************************/ /* */ void ReadClient (struct WsLibStruct *wsptr) { CLIENT_DATA *clptr; /*********/ /* begin */ /*********/ if (dbug) fprintf (stdout, "ReadClient() %%X%08.08X\n", WsLibReadStatus (wsptr)); if (VMSnok (WsLibReadStatus (wsptr))) { WsLibClose (wsptr, 0, NULL); return; } clptr = WsLibGetUserData (wsptr); /* ensure it's null-terminated */ clptr->InputBuffer[WsLibReadCount(wsptr)] = '\0'; if (strstr (clptr->InputBuffer, "detail=1")) { clptr->ProvideDetailData = true; JsonDetailData (); WsLibWrite (wsptr, SystemData.DetailBuffer, SystemData.DetailBufferLength, WSLIB_ASYNCH); } else if (strstr (clptr->InputBuffer, "detail=0")) clptr->ProvideDetailData = false; if (strstr (clptr->InputBuffer, "instance=1")) { clptr->ProvideInstanceData = true; JsonInstanceData (); WsLibWrite (wsptr, SystemData.InstanceBuffer, SystemData.InstanceBufferLength, WSLIB_ASYNCH); } else if (strstr (clptr->InputBuffer, "instance=0")) clptr->ProvideInstanceData = false; if (strstr (clptr->InputBuffer, "proxy=1")) { clptr->ProvideProxyData = true; JsonProxyData (true); WsLibWrite (wsptr, SystemData.ProxyBuffer, SystemData.ProxyBufferLength, WSLIB_ASYNCH); } else if (strstr (clptr->InputBuffer, "proxy=0")) clptr->ProvideProxyData = false; if (strstr (clptr->InputBuffer, "request=1")) { clptr->ProvideRequestData = true; JsonRequestData (clptr, true); WsLibWrite (wsptr, SystemData.RequestBuffer, SystemData.RequestBufferLength, WSLIB_ASYNCH); } else if (strstr (clptr->InputBuffer, "request=0")) clptr->ProvideRequestData = false; if (strstr (clptr->InputBuffer, "script=1")) { clptr->ProvideScriptData = true; JsonScriptData (); WsLibWrite (wsptr, SystemData.ScriptBuffer, SystemData.ScriptBufferLength, WSLIB_ASYNCH); } else if (strstr (clptr->InputBuffer, "script=0")) clptr->ProvideScriptData = false; if (strstr (clptr->InputBuffer, "status=1")) { clptr->ProvideInstanceStatus = true; JsonInstanceStatus (clptr->WorldAccess); WsLibWrite (wsptr, SystemData.StatusBuffer, SystemData.StatusBufferLength, WSLIB_ASYNCH); } else if (strstr (clptr->InputBuffer, "status=0")) clptr->ProvideInstanceStatus = false; if (strstr (clptr->InputBuffer, "webdav=1")) { clptr->ProvideWebDavData = true; JsonWebDavData (); WsLibWrite (wsptr, SystemData.WebDavBuffer, SystemData.WebDavBufferLength, WSLIB_ASYNCH); } else if (strstr (clptr->InputBuffer, "webdav=0")) clptr->ProvideWebDavData = false; /* read next */ WsLibRead (wsptr, clptr->InputBuffer, sizeof(clptr->InputBuffer)-1, ReadClient); } /*****************************************************************************/ /* For an XMLHttpRequest() HTTP request. The response body will stream chunks of JSON data to be parsed by the browser JavaScript receiving it. Do the best we can to disable proxy caching ensuring a timely flow of response data. */ void XhrResponse () { /*********/ /* begin */ /*********/ if (dbug) fprintf (stdout, "XhrResponse()\n"); fprintf (stdout, "Status: 200\n\ Content-Type: text/html\n\ Script-Control: X-stream-mode=1\n\ Script-Control: X-content-encoding-gzip=0\n\ Pragma: no-cache\n\ Cache-Control: no-cache, no-store, private\n\ \n"); fprintf (stdout, "%s%s", SystemData.StaticBuffer, JSON_SENTINAL); fflush (stdout); CgiClientData.Initialised = TRUE; ConnectedCount++; } /*****************************************************************************/ /* Send a JSON data structure containing a message to be displayed as an alert. */ void JsonAlert ( CLIENT_DATA *ClientPtr, char *FormatString, ... ) { static char CurTime [24]; static $DESCRIPTOR (CurTimeDsc, CurTime); static $DESCRIPTOR (CurTimeFaoDsc, "!20%D\0"); int len; char jsonbuf [512], msgbuf [256]; va_list argptr; CLIENT_DATA *clptr; struct WsLibStruct *wsctx, *wsptr; /*********/ /* begin */ /*********/ if (dbug) fprintf (stdout, "JsonAlert()\n"); va_start (argptr, FormatString); len = vsnprintf (msgbuf, sizeof(msgbuf), FormatString, argptr); if (len >= sizeof(msgbuf)) EXIT_FI_LI (SS$_RESULTOVF); sys$fao (&CurTimeFaoDsc, 0, &CurTimeDsc, &CurrentTime64); if (CurTime[0] == ' ') CurTime[0] = '0'; len = snprintf (jsonbuf, sizeof(jsonbuf), "{\"$data\":\"alert\",\"time\":\"%s\",\"message\":\"%s\"}", CurTime, msgbuf); if (len >= sizeof(jsonbuf)) EXIT_FI_LI (SS$_RESULTOVF); if (DataViaCGI) { if (CgiClientData.Initialised) { fputs (jsonbuf, stdout); fputs (JSON_SENTINAL, stdout); fflush (stdout); } } wsctx = NULL; while (wsptr = WsLibNext(&wsctx)) { clptr = WsLibGetUserData (wsptr); if (!clptr->Initialised) continue; if (ClientPtr && clptr != ClientPtr) continue; /* set and forget; buffer each message internally */ WsLibSetBuffer (clptr->WsLibPtr); WsLibWrite (wsptr, jsonbuf, len, WSLIB_ASYNCH); /* revert to requiring an external, persistent buffer */ WsLibSetNoBuffer (clptr->WsLibPtr); } } /*****************************************************************************/ /* Indepdendent, timer AST driven update of the system performance data. This data is then written to connected clients. */ void ProvideData () { static const long cvtf_mode = LIB$K_DELTA_SECONDS_F; static ulong PrevTimeStamp; static int64 PrevTime64; int idx, status; int64 DiffTime64; ulong fltf; /* generic 32 bits for the VAX float */ char *cptr, *sptr, *zptr; struct WsLibStruct *wsctx, *wsptr; CLIENT_DATA *clptr; /*********/ /* begin */ /*********/ if (dbug) fprintf (stdout, "ProvideData()\n"); if (CheckHttpdPid() == SS$_NONEXPR) { /* server process has changed so remap global section in case deleted */ MapGlobalSection (); if (!HttpdGblSecPtr) { /* currently no global section so just wait */ SetInterval (); return; } } GetTimFunction (&CurrentTime64); sys$numtim (&CurrentTime7, &CurrentTime64); TimeStamp = decc$fix_time (&CurrentTime64); /* alert if the refresh period has been overly long */ if (TimeStamp > PrevTimeStamp + (RefreshInterval * REFRESH_ALERT)) if (PrevTimeStamp) JsonAlert (NULL, "!Refresh lasted %d seconds!", TimeStamp - PrevTimeStamp); PrevTimeStamp = TimeStamp; if (!PrevTime64) { PrevTime64 = CurrentTime64; if (dbug) fprintf (stdout, "%d %s\n", SystemData.StaticBufferLength, SystemData.StaticBuffer); } /* calculate a floating point delta time */ status = lib$sub_times (&CurrentTime64, &PrevTime64, &DiffTime64); if (VMSnok (status)) EXIT_FI_LI (status); PrevTime64 = CurrentTime64; #ifdef __ALPHA /* Alpha 7.3-1 does not have lib$cvts_..() */ lib$cvtf_from_internal_time (&cvtf_mode, &fltf, &DiffTime64); /* convert VAX to IEEE float */ status = cvt$convert_float (&fltf, CVT$K_VAX_F, &DeltaSeconds, CVT$K_IEEE_S, CVT$M_ROUND_TO_NEAREST); #else status = lib$cvts_from_internal_time (&cvtf_mode, &DeltaSeconds, &DiffTime64); #endif if (VMSnok (status)) EXIT_FI_LI (status); if (dbug) fprintf (stdout, "DeltaSeconds: %f\n", DeltaSeconds); /***********/ /* collect */ /***********/ CollectSPI (); CollectNetIntData (); CollectInstanceData (); if (DataViaCGI) { /*******/ /* CGI */ /*******/ /* collect it all */ CollectProxyData (); /* data dependency pre CollectAccounting() */ CollectScriptData (); CollectWebDavData (); /* format it all */ JsonDetailData (); JsonInstanceData (); JsonInstanceStatus (WorldAccess); JsonProxyData (false); JsonRequestData (NULL, false); JsonScriptData (); JsonWebDavData (); } else { /*************/ /* WebSocket */ /*************/ /* only collect these data if a client requires them */ for (wsctx = NULL; wsptr = WsLibNext(&wsctx);) if (((CLIENT_DATA*)WsLibGetUserData(wsptr))->ProvideDetailData) { JsonDetailData (); break; } for (wsctx = NULL; wsptr = WsLibNext(&wsctx);) if (((CLIENT_DATA*)WsLibGetUserData(wsptr))->ProvideInstanceData) { JsonInstanceData (); break; } for (wsctx = NULL; wsptr = WsLibNext(&wsctx);) if ((clptr = (CLIENT_DATA*)WsLibGetUserData(wsptr))->ProvideInstanceStatus) { JsonInstanceStatus (clptr->WorldAccess); break; } for (wsctx = NULL; wsptr = WsLibNext(&wsctx);) if (((CLIENT_DATA*)WsLibGetUserData(wsptr))->ProvideProxyData) { CollectProxyData (); /* data dependency pre CollectAccounting() */ JsonProxyData (false); break; } for (wsctx = NULL; wsptr = WsLibNext(&wsctx);) if ((clptr = (CLIENT_DATA*)WsLibGetUserData(wsptr))->ProvideRequestData) { JsonRequestData (clptr, false); break; } for (wsctx = NULL; wsptr = WsLibNext(&wsctx);) if (((CLIENT_DATA*)WsLibGetUserData(wsptr))->ProvideScriptData) { CollectScriptData (); JsonScriptData (); break; } for (wsctx = NULL; wsptr = WsLibNext(&wsctx);) if (((CLIENT_DATA*)WsLibGetUserData(wsptr))->ProvideWebDavData) { CollectWebDavData (); JsonWebDavData (); break; } } CollectAccounting (); JsonSummaryData (); /**********/ /* alerts */ /**********/ if (SystemData.InstanceStartupCount > SystemData.PrevInstanceStartupCount) JsonAlert (NULL, "!Server instance startup from %d to %d", SystemData.PrevInstanceStartupCount, SystemData.InstanceStartupCount); SystemData.PrevInstanceStartupCount = SystemData.InstanceStartupCount; if (SystemData.TotalDclHardLimit > SystemData.PrevTotalDclHardLimit) JsonAlert (NULL, "!DCL scripting hard-limit hit from %d to %d", SystemData.PrevTotalDclHardLimit, SystemData.TotalDclHardLimit); SystemData.PrevTotalDclHardLimit = SystemData.TotalDclHardLimit; if (SystemData.StatusCode403 > SystemData.PrevStatusCode403 + AlertStatusCode403) { JsonAlert (NULL, "!HTTP status 403 exceeded %d/minute", AlertStatusCode403); SystemData.PrevStatusCode403 = SystemData.StatusCode403; } else if (CurrentTime7[4] != SystemData.PrevMinute) SystemData.PrevStatusCode403 = SystemData.StatusCode403; if (SystemData.StatusCodeGroup[5] > SystemData.PrevStatusCodeGroup5 + AlertStatusCodeGroup5) { JsonAlert (NULL, "!HTTP status 5nn exceeded %d/minute", AlertStatusCodeGroup5); SystemData.PrevStatusCodeGroup5 = SystemData.StatusCodeGroup[5]; } else if (CurrentTime7[4] != SystemData.PrevMinute) SystemData.PrevStatusCodeGroup5 = SystemData.StatusCodeGroup[5]; if (AlertStatusCodeCount) { /* alerts for ALAMODE_ALERT_NNN defined HTTP statuses */ for (idx = 0; idx < AlertStatusCodeCount; idx++) { if (SystemData.AlertStatusCode[idx].current > SystemData.AlertStatusCode[idx].previous + SystemData.AlertStatusCode[idx].trigger) { JsonAlert (NULL, "!HTTP status %d exceeded %d/minute", SystemData.AlertStatusCode[idx].code, SystemData.AlertStatusCode[idx].trigger); SystemData.AlertStatusCode[idx].previous = SystemData.AlertStatusCode[idx].current; } else if (CurrentTime7[4] != SystemData.PrevMinute) SystemData.AlertStatusCode[idx].previous = SystemData.AlertStatusCode[idx].current; } } GetTimFunction (&CollectTime64); /**********/ /* supply */ /**********/ if (DataViaCGI) { /*******/ /* CGI */ /*******/ /* puts three backslashes into the stream - impossible JSON data */ if (SystemData.SummaryBufferLength) { fputs (SystemData.SummaryBuffer, stdout); fputs (JSON_SENTINAL, stdout); } if (SystemData.DetailBufferLength) { fputs (SystemData.DetailBuffer, stdout); fputs (JSON_SENTINAL, stdout); } if (SystemData.InstanceBufferLength) { fputs (SystemData.InstanceBuffer, stdout); fputs (JSON_SENTINAL, stdout); } if (SystemData.ProxyBufferLength) { fputs (SystemData.ProxyBuffer, stdout); fputs (JSON_SENTINAL, stdout); } if (SystemData.RequestBufferLength) { fputs (SystemData.RequestBuffer, stdout); fputs (JSON_SENTINAL, stdout); } if (SystemData.ScriptBufferLength) { fputs (SystemData.ScriptBuffer, stdout); fputs (JSON_SENTINAL, stdout); } if (SystemData.StatusBufferLength) { fputs (SystemData.StatusBuffer, stdout); fputs (JSON_SENTINAL, stdout); } if (SystemData.WebDavBufferLength) { fputs (SystemData.WebDavBuffer, stdout); fputs (JSON_SENTINAL, stdout); } if (SystemData.RuntimeBufferLength) { fprintf (stdout, "%s,\"CGIplus\":%s}", SystemData.RuntimeBuffer, IsCgiPlus ? "true" : "false"); fputs (JSON_SENTINAL, stdout); } fflush (stdout); } /* only supply (some of) these data if the client requires them */ for (wsctx = NULL; wsptr = WsLibNext(&wsctx);) { /*************/ /* WebSocket */ /*************/ clptr = WsLibGetUserData (wsptr); zptr = (sptr = clptr->OutputBuffer) + sizeof(clptr->OutputBuffer)-1; for (cptr = SystemData.SummaryBuffer; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = JSON_SENTINAL; *cptr && sptr < zptr; *sptr++ = *cptr++); if (clptr->ProvideDetailData && SystemData.DetailBufferLength) { for (cptr = SystemData.DetailBuffer; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = JSON_SENTINAL; *cptr && sptr < zptr; *sptr++ = *cptr++); } if (clptr->ProvideInstanceData && SystemData.InstanceBufferLength) { for (cptr = SystemData.InstanceBuffer; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = JSON_SENTINAL; *cptr && sptr < zptr; *sptr++ = *cptr++); } if (clptr->ProvideProxyData && SystemData.ProxyBufferLength) { for (cptr = SystemData.ProxyBuffer; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = JSON_SENTINAL; *cptr && sptr < zptr; *sptr++ = *cptr++); } if (clptr->ProvideRequestData && SystemData.RequestBufferLength) { for (cptr = SystemData.RequestBuffer; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = JSON_SENTINAL; *cptr && sptr < zptr; *sptr++ = *cptr++); } if (clptr->ProvideScriptData && SystemData.ScriptBufferLength) { for (cptr = SystemData.ScriptBuffer; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = JSON_SENTINAL; *cptr && sptr < zptr; *sptr++ = *cptr++); } if (clptr->ProvideInstanceStatus && SystemData.StatusBufferLength) { for (cptr = SystemData.StatusBuffer; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = JSON_SENTINAL; *cptr && sptr < zptr; *sptr++ = *cptr++); } if (clptr->ProvideWebDavData && SystemData.WebDavBufferLength) { for (cptr = SystemData.WebDavBuffer; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = JSON_SENTINAL; *cptr && sptr < zptr; *sptr++ = *cptr++); } if (SystemData.RuntimeBufferLength) { for (cptr = SystemData.RuntimeBuffer; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = ",\"WebSocket\":true}"; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = JSON_SENTINAL; *cptr && sptr < zptr; *sptr++ = *cptr++); } if (sptr > zptr) EXIT_FI_LI (SS$_RESULTOVF); WsLibWrite (wsptr, clptr->OutputBuffer, sptr - clptr->OutputBuffer, WSLIB_ASYNCH); } if (dbug) Displaydbug(); /*************/ /* and again */ /*************/ SetInterval (); ProvideDataSeconds += RefreshInterval; GetTimFunction (&ProvideTime64); JsonRuntime (); } /*****************************************************************************/ /* Provide some runtime data. Average duration of each data collection and total monitor cycle (to differentiate the time required to write the data to the client(s)), average CPU consumed, average user and non-user CPU modes. All in seconds. With VAX clock granularity, durations may fail to register if sub-10mS. This data is not available to the end-user, is for developer and technical insight purposes, and can be seen from the data stream. */ void JsonRuntime () { static int64 CollectTotalTime64, ProvideTotalTime64; static long CpuSekInit, CpuTimInit, CpuUsrInit, JpiCpuTim, JpiExecTim, JpiKrnlTim, JpiSuprTim, JpiUserTim, StatCount; static struct { short BufferLength; short ItemCode; void *BufferPtr; void *LengthPtr; } ItemListJpi[] = { { sizeof(JpiCpuTim), JPI$_CPUTIM, &JpiCpuTim, 0 }, { sizeof(JpiExecTim), JPI$_EXECTIM, &JpiExecTim, 0 }, { sizeof(JpiKrnlTim), JPI$_KRNLTIM, &JpiKrnlTim, 0 }, { sizeof(JpiSuprTim), JPI$_SUPRTIM, &JpiSuprTim, 0 }, { sizeof(JpiUserTim), JPI$_USERTIM, &JpiUserTim, 0 }, {0,0,0,0} }; int status; int64 RunTime64, DiffTime64; ulong CpuUSR, CpuSEK, CpuTIM; char *sptr, *zptr; float fcount; IOSBLK IOsb; /*********/ /* begin */ /*********/ if (dbug) fprintf (stdout, "\nJsonRuntime()\n"); status = sys$getjpiw ( EfnWait, /* efn */ 0, /* pidaddr */ 0, /* prcnam */ &ItemListJpi, /* item list */ &IOsb, /* iosb */ 0, /* astaddr */ 0 ); /* astprm */ if (VMSok (status)) status = IOsb.iosb$w_status; if (VMSnok (status)) EXIT_FI_LI (status); if ((fcount = (float)(StatCount++)) == 0.0) { /* track and offset initial setup overheads */ CpuTimInit = JpiCpuTim; CpuUsrInit = JpiUserTim; CpuSekInit = JpiSuprTim + JpiExecTim + JpiKrnlTim; return; } CollectTime64 -= CurrentTime64; CollectTotalTime64 += CollectTime64; ProvideTime64 -= CurrentTime64; ProvideTotalTime64 += ProvideTime64; lib$sub_times (&CurrentTime64, &StartTime64, &RunTime64); CpuTIM = JpiCpuTim - CpuTimInit; CpuUSR = JpiUserTim - CpuUsrInit; CpuSEK = JpiSuprTim + JpiExecTim + JpiKrnlTim - CpuSekInit; zptr = (sptr = SystemData.RuntimeBuffer) + sizeof(SystemData.RuntimeBuffer); sptr += snprintf (sptr, zptr-sptr, "{\"$data\":\"runtime\",\ \"cpu\":%f,\ \"cpuTIM\":%f,", (float)JpiCpuTim / 100.0, (float)CpuTIM / 100.0 / fcount); sptr += snprintf (sptr, zptr-sptr, "\"cpuUSR\":%f,\ \"cpuSEK\":%f,", (float)CpuUSR / 100.0 / fcount, (float)CpuSEK / 100.0 / fcount); sptr += snprintf (sptr, zptr-sptr, "\"run\":\"%s\",\ \"collect\":%f,\ \"provide\":%f,\ \"usage\":%d,\ \"connected\":%d,\ \"lck$m_xvalblk\":%s", MungeTime(&RunTime64), (float)CollectTotalTime64 / 10000000.0 / fcount, (float)ProvideTotalTime64 / 10000000.0 / fcount, UsageCount, ConnectedCount, SystemData.LkiValBlkSize == 64 ? "true" : "false"); if (sptr >= zptr) EXIT_FI_LI (SS$_RESULTOVF); SystemData.RuntimeBufferLength = sptr - SystemData.RuntimeBuffer; } /*****************************************************************************/ /* For the command-line debug mode. */ void Displaydbug () { JsonDetailData (); JsonInstanceData (); JsonInstanceStatus (WorldAccess); JsonProxyData (); JsonRequestData (NULL, true); JsonScriptData (); JsonWebDavData (); fprintf (stdout, "*****%d %s\n", SystemData.SummaryBufferLength, SystemData.SummaryBuffer); fprintf (stdout, "*****%d %s\n", SystemData.DetailBufferLength, SystemData.DetailBuffer); fprintf (stdout, "*****%d %s\n", SystemData.InstanceBufferLength, SystemData.InstanceBuffer); fprintf (stdout, "*****%d %s\n", SystemData.StatusBufferLength, SystemData.StatusBuffer); fprintf (stdout, "*****%d %s\n", SystemData.ProxyBufferLength, SystemData.ProxyBuffer); fprintf (stdout, "*****%d %s\n", SystemData.RequestBufferLength, SystemData.RequestBuffer); fprintf (stdout, "*****%d %s\n", SystemData.ScriptBufferLength, SystemData.ScriptBuffer); fprintf (stdout, "*****%d %s\n", SystemData.WebDavBufferLength, SystemData.WebDavBuffer); } /*****************************************************************************/ /* Create a JSON data structure in a buffer representing the per-boot characteristics of the system. */ void JsonStaticData () { static char BootTime [24]; static $DESCRIPTOR (BootTimeFaoDsc, "!17%D\0"); static $DESCRIPTOR (BootTimeDsc, BootTime); uint64 Size64; char *cptr, *sptr, *zptr; /*********/ /* begin */ /*********/ if (dbug) fprintf (stdout, "JsonStaticData()\n"); zptr = (sptr = SystemData.HttpdVersion) + sizeof(SystemData.HttpdVersion)-1; for (cptr = gblptr->HttpdVersion; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; Size64 = (uint64)SystemData.MemSize * SystemData.PageSize; zptr = (sptr = SystemData.StaticBuffer) + sizeof(SystemData.StaticBuffer); sptr += snprintf (sptr, zptr-sptr, "{\"$data\":\"static\""); sptr += snprintf (sptr, zptr-sptr, ",\"WASD\":\"%s\"", SystemData.HttpdVersion); sptr += snprintf (sptr, zptr-sptr, ",\"alamode\":\"%s\"", SOFTWAREVN); sptr += snprintf (sptr, zptr-sptr, ",\"build\":\"%s\"", BUILDING_FOR); sptr += snprintf (sptr, zptr-sptr, ",\"cpuAvail\":%d", SystemData.AvailCpuCount); sptr += snprintf (sptr, zptr-sptr, ",\"archName\":\"%s\"", SystemData.ArchName); sptr += snprintf (sptr, zptr-sptr, ",\"hwName\":\"%s\"", SystemData.HwName); sptr += snprintf (sptr, zptr-sptr, ",\"interval\":%d", RefreshInterval); sptr += snprintf (sptr, zptr-sptr, ",\"jpiPID\":\"%08.08X\"", SystemData.JpiPid); sptr += snprintf (sptr, zptr-sptr, ",\"nodeName\":\"%s\"", SystemData.NodeName); sptr += snprintf (sptr, zptr-sptr, ",\"vmsVersion\":\"%s\"", SystemData.VmsVersion); sptr += snprintf (sptr, zptr-sptr, ",\"memSize\":%lld", Size64); sys$fao (&BootTimeFaoDsc, 0, &BootTimeDsc, &SystemData.BootTime64); sptr += snprintf (sptr, zptr-sptr, ",\"bootTime\":\"%s\"", BootTime); sptr += snprintf (sptr, zptr-sptr, ",\"proxyEnabled\":%s", SystemData.ProxyEnabled ? "true" : "false"); sptr += snprintf (sptr, zptr-sptr, ",\"webdavEnabled\":%s", SystemData.WebDavEnabled ? "true" : "false"); if (SystemData.AlamodeQueryPtr) sptr += snprintf (sptr, zptr-sptr, ",\"ALAMODE_QUERY\":\"%s\"", SystemData.AlamodeQueryPtr); sptr += snprintf (sptr, zptr-sptr, ",\"statusReport\":true"); sptr += snprintf (sptr, zptr-sptr, "}"); if (sptr >= zptr) EXIT_FI_LI (SS$_RESULTOVF); SystemData.StaticBufferLength = sptr - SystemData.StaticBuffer; } /*****************************************************************************/ /* Create a JSON data structure in a buffer representing the current state of the system and server. The data provided is "sparse" in the sense that any datum whose value is zero is not explicitly supplied. The JavaScript receiving the data assumes that any datum it accesses as 'undefined' is zero. */ void JsonSummaryData () { static char CurTime [24], UpTime [16]; static $DESCRIPTOR (CurTimeFaoDsc, "!20%D\0"); static $DESCRIPTOR (UpTimeFaoDsc, "!10%D\0"); static $DESCRIPTOR (CurTimeDsc, CurTime); static $DESCRIPTOR (UpTimeDsc, UpTime); int64 InUse64; char *cptr, *sptr, *zptr; /*********/ /* begin */ /*********/ if (dbug) fprintf (stdout, "JsonSummaryData()\n"); InUse64 = SystemData.MemInUse * SystemData.PageSize; SystemData.SummaryBufferLength = 0; zptr = (sptr = SystemData.SummaryBuffer) + sizeof(SystemData.SummaryBuffer); sptr += snprintf (sptr, zptr-sptr, "{\"$data\":\"summary\""); /***************/ /* system data */ /***************/ sptr += snprintf (sptr, zptr-sptr, ",\"timestamp\":%u", TimeStamp); sptr += snprintf (sptr, zptr-sptr, ",\"usage\":%d", UsageCount); sptr += snprintf (sptr, zptr-sptr, ",\"count\":%d", ConnectedCount); sptr += snprintf (sptr, zptr-sptr, ",\"cpuActive\":%d", SystemData.ActiveCpuCount); if (SystemData.CpuBusyPercent) sptr += snprintf (sptr, zptr-sptr, ",\"cpuBusy\":%d", SystemData.CpuBusyPercent); if (SystemData.CpuBusyPercent - SystemData.CpuUserPercent) sptr += snprintf (sptr, zptr-sptr, ",\"cpuSEK\":%d", SystemData.CpuBusyPercent - SystemData.CpuUserPercent); if (SystemData.BufferedIO) sptr += snprintf (sptr, zptr-sptr, ",\"bio\":%u", SystemData.BufferedIO); if (SystemData.FcpRead + SystemData.FcpWrite) sptr += snprintf (sptr, zptr-sptr, ",\"fcpRW\":%u", SystemData.FcpRead + SystemData.FcpWrite); if (SystemData.FcpWrite) sptr += snprintf (sptr, zptr-sptr, ",\"fcpWrite\":%u", SystemData.FcpWrite); sptr += snprintf (sptr, zptr-sptr, ",\"memInUse\":%lld", InUse64); sptr += snprintf (sptr, zptr-sptr, ",\"sysProcs\":%d", SystemData.ProcessCount); sptr += snprintf (sptr, zptr-sptr, ",\"webProcs\":%d", SystemData.InstanceNodeCount + SystemData.DclProcessCurrent, SystemData.CurrentDECnetTasks); sptr += snprintf (sptr, zptr-sptr, ",\"netPerSec\":%d", SystemData.NetBytesPerSec); sptr += snprintf (sptr, zptr-sptr, ",\"webPerSec\":%d", SystemData.BytesRawPerSec); sptr += snprintf (sptr, zptr-sptr, ",\"netDgramRx\":%d", SystemData.NetDatagramRx); sptr += snprintf (sptr, zptr-sptr, ",\"netDgramTx\":%d", SystemData.NetDatagramTx); sptr += snprintf (sptr, zptr-sptr, ",\"webBlockRead\":%d", SystemData.BlockReadSize); sptr += snprintf (sptr, zptr-sptr, ",\"webBlockWrite\":%d", SystemData.BlockWriteSize); sys$fao (&UpTimeFaoDsc, 0, &UpTimeDsc, &SystemData.UpTime64); for (cptr = UpTime; *cptr && *cptr == ' '; cptr++); sptr += snprintf (sptr, zptr-sptr, ",\"upTime\":\"%s\"", cptr); sys$fao (&CurTimeFaoDsc, 0, &CurTimeDsc, &CurrentTime64); if (CurTime[0] == ' ') CurTime[0] = '0'; sptr += snprintf (sptr, zptr-sptr, ",\"vmsTime\":\"%s\"", CurTime); /***************/ /* server data */ /***************/ sptr += snprintf (sptr, zptr-sptr, ",\"instanceCluster\":%d", SystemData.InstanceClusterCount); sptr += snprintf (sptr, zptr-sptr, ",\"instanceNode\":%d", SystemData.InstanceNodeCount); sptr += snprintf (sptr, zptr-sptr, ",\"instanceStartup\":%d", SystemData.InstanceStartupCount); if (SystemData.InstanceNodeCount > 1) sptr += snprintf (sptr, zptr-sptr, ",\"instanceNodeSuper\":\"%08.08X\"", SystemData.InstanceNodeSuperPid); if (SystemData.InstancePassive) sptr += snprintf (sptr, zptr-sptr, ",\"instancePassive\":true"); if (SystemData.ConnectSuspend) sptr += snprintf (sptr, zptr-sptr, ",\"connectSuspend\":true"); if (SystemData.TotalFile) sptr += snprintf (sptr, zptr-sptr, ",\"totalFile\":%d", SystemData.TotalFile); sptr += snprintf (sptr, zptr-sptr, ",\"totalRequest\":%d", SystemData.ProcessingTotal); sptr += snprintf (sptr, zptr-sptr, ",\"totalHttp1Request\":%d", SystemData.ProcessingHttp1Total); sptr += snprintf (sptr, zptr-sptr, ",\"totalHttp2Request\":%d", SystemData.ProcessingHttp2Total); if (SystemData.TotalAllScript) sptr += snprintf (sptr, zptr-sptr, ",\"totalScript\":%d", SystemData.TotalAllScript); if (SystemData.SslCount) sptr += snprintf (sptr, zptr-sptr, ",\"totalSSL\":%d", SystemData.SslCount); if (SystemData.TotalAllOther) sptr += snprintf (sptr, zptr-sptr, ",\"totalOther\":%d", SystemData.TotalAllOther); if (SystemData.TotalProxy) sptr += snprintf (sptr, zptr-sptr, ",\"totalProxy\":%d", SystemData.TotalProxy); if (SystemData.TotalWebDav) sptr += snprintf (sptr, zptr-sptr, ",\"totalWebDAV\":%d", SystemData.TotalWebDav); if (SystemData.TotalThrottled) sptr += snprintf (sptr, zptr-sptr, ",\"totalThrottled\":%d", SystemData.TotalThrottled); if (SystemData.TotalThrottleQueued) sptr += snprintf (sptr, zptr-sptr, ",\"totalThrottleQueued\":%d", SystemData.TotalThrottleQueued); if (SystemData.TotalThrottleBusy) sptr += snprintf (sptr, zptr-sptr, ",\"totalThrottleBusy\":%d", SystemData.TotalThrottleBusy); sptr += snprintf (sptr, zptr-sptr, ",\"bytesRx\":%lld", SystemData.BytesRawRx64); sptr += snprintf (sptr, zptr-sptr, ",\"bytesTx\":%lld", SystemData.BytesRawTx64); sptr += snprintf (sptr, zptr-sptr, ",\"statusCodes\":[%d,%d,%d,%d,%d,%d]", SystemData.StatusCodeGroup[0], SystemData.StatusCodeGroup[1], SystemData.StatusCodeGroup[2], SystemData.StatusCodeGroup[3], SystemData.StatusCodeGroup[4], SystemData.StatusCodeGroup[5]); if (SystemData.StatusCode403) sptr += snprintf (sptr, zptr-sptr, ",\"status403\":%d", SystemData.StatusCode403); /****************/ /* current data */ /****************/ sptr += snprintf (sptr, zptr-sptr, ",\"nowConnect\":%d", SystemData.CurrentConnected); sptr += snprintf (sptr, zptr-sptr, ",\"nowHttp1Connect\":%d", SystemData.CurrentHttp1Connected); sptr += snprintf (sptr, zptr-sptr, ",\"nowHttp2Connect\":%d", SystemData.CurrentHttp2Connected); sptr += snprintf (sptr, zptr-sptr, ",\"peakConnect\":%d", SystemData.ConnectPeak); sptr += snprintf (sptr, zptr-sptr, ",\"peakHttp1Connect\":%d", SystemData.ConnectHttp1Peak); sptr += snprintf (sptr, zptr-sptr, ",\"peakHttp2Connect\":%d", SystemData.ConnectHttp2Peak); sptr += snprintf (sptr, zptr-sptr, ",\"totalConnect\":%d", SystemData.ConnectTotal); sptr += snprintf (sptr, zptr-sptr, ",\"totalHttp1Connect\":%d", SystemData.ConnectHttp1Total); sptr += snprintf (sptr, zptr-sptr, ",\"totalHttp2Connect\":%d", SystemData.ConnectHttp2Total); if (SystemData.CurrentProcessing) sptr += snprintf (sptr, zptr-sptr, ",\"nowProcess\":%d", SystemData.CurrentProcessing); if (SystemData.CurrentHttp1Processing) sptr += snprintf (sptr, zptr-sptr, ",\"nowHttp1Process\":%d", SystemData.CurrentHttp1Processing); if (SystemData.CurrentHttp2Processing) sptr += snprintf (sptr, zptr-sptr, ",\"nowHttp2Process\":%d", SystemData.CurrentHttp2Processing); sptr += snprintf (sptr, zptr-sptr, ",\"peakProcess\":%d", SystemData.ProcessingPeak); sptr += snprintf (sptr, zptr-sptr, ",\"peakHttp1Process\":%d", SystemData.ProcessingHttp1Peak); sptr += snprintf (sptr, zptr-sptr, ",\"peakHttp2Process\":%d", SystemData.ProcessingHttp2Peak); sptr += snprintf (sptr, zptr-sptr, ",\"totalProcess\":%d", SystemData.ProcessingTotal); sptr += snprintf (sptr, zptr-sptr, ",\"totalHttp1Process\":%d", SystemData.ProcessingHttp1Total); sptr += snprintf (sptr, zptr-sptr, ",\"totalHttp2Process\":%d", SystemData.ProcessingHttp2Total); if (SystemData.DeltaFile) sptr += snprintf (sptr, zptr-sptr, ",\"deltaFile\":%d", SystemData.DeltaFile); if (SystemData.DeltaOther) sptr += snprintf (sptr, zptr-sptr, ",\"deltaOther\":%d", SystemData.DeltaOther); if (SystemData.DeltaRequest) sptr += snprintf (sptr, zptr-sptr, ",\"deltaRequest\":%d", SystemData.DeltaRequest); if (SystemData.DeltaRequestFail) sptr += snprintf (sptr, zptr-sptr, ",\"deltaRequestFail\":%d", SystemData.DeltaRequestFail); if (SystemData.DeltaHttp) sptr += snprintf (sptr, zptr-sptr, ",\"deltaHttp\":%d", SystemData.DeltaHttp); if (SystemData.DeltaHttp1) sptr += snprintf (sptr, zptr-sptr, ",\"deltaHttp1\":%d", SystemData.DeltaHttp1); if (SystemData.DeltaHttp2) sptr += snprintf (sptr, zptr-sptr, ",\"deltaHttp2\":%d", SystemData.DeltaHttp2); if (SystemData.DeltaThrottled) sptr += snprintf (sptr, zptr-sptr, ",\"deltaThrottled\":%d", SystemData.DeltaThrottled); if (SystemData.DeltaThrottleQueued) sptr += snprintf (sptr, zptr-sptr, ",\"deltaThrottleQueued\":%d", SystemData.DeltaThrottleQueued); if (SystemData.DeltaThrottleBusy) sptr += snprintf (sptr, zptr-sptr, ",\"deltaThrottleBusy\":%d", SystemData.DeltaThrottleBusy); if (SystemData.CurrentThrottled) sptr += snprintf (sptr, zptr-sptr, ",\"nowThrottled\":%d", SystemData.CurrentThrottled); if (SystemData.CurrentThrottleProcessing) sptr += snprintf (sptr, zptr-sptr, ",\"nowThrottledProcessing\":%d", SystemData.CurrentThrottleProcessing); if (SystemData.CurrentThrottleQueued) sptr += snprintf (sptr, zptr-sptr, ",\"nowThrottledQueued\":%d", SystemData.CurrentThrottleQueued); if (SystemData.CurrentThrottleBusyMetric) sptr += snprintf (sptr, zptr-sptr, ",\"nowThrottledBusy\":%d", SystemData.CurrentThrottleBusyMetric); if (SystemData.DeltaWebDavTotal) sptr += snprintf (sptr, zptr-sptr, ",\"deltaWebDAVtotal\":%d", SystemData.DeltaWebDavTotal); if (SystemData.DeltaWebDavRead) sptr += snprintf (sptr, zptr-sptr, ",\"deltaWebDAVread\":%d", SystemData.DeltaWebDavRead); if (SystemData.DeltaWebDavWrite) sptr += snprintf (sptr, zptr-sptr, ",\"deltaWebDAVwrite\":%d", SystemData.DeltaWebDavWrite); if (SystemData.TotalWebSocket) sptr += snprintf (sptr, zptr-sptr, ",\"totalWebSocket\":%d", SystemData.TotalWebSocket); if (SystemData.CurrentWebSockets) sptr += snprintf (sptr, zptr-sptr, ",\"nowWebSocket\":%d", SystemData.CurrentWebSockets); /****************/ /* delta script */ /****************/ if (SystemData.DeltaAllScript) sptr += snprintf (sptr, zptr-sptr, ",\"deltaScript\":%d", SystemData.DeltaAllScript); if (SystemData.DeltaScriptCgi) sptr += snprintf (sptr, zptr-sptr, ",\"deltaScriptCGI\":%d", SystemData.DeltaScriptCgi); if (SystemData.DeltaDECnetCgi + SystemData.DeltaDECnetOsu) sptr += snprintf (sptr, zptr-sptr, ",\"deltaDECnet\":%d", SystemData.DeltaDECnetCgi + SystemData.DeltaDECnetOsu); if (SystemData.DeltaDECnetOsu) sptr += snprintf (sptr, zptr-sptr, ",\"deltaDECnetOSU\":%d", SystemData.DeltaDECnetOsu); if (SystemData.DeltaCgiPlus + SystemData.DeltaRte) sptr += snprintf (sptr, zptr-sptr, ",\"deltaCGIplusRTE\":%d", SystemData.DeltaCgiPlus + SystemData.DeltaRte); if (SystemData.DeltaCgiPlusReused + SystemData.DeltaRteReused) sptr += snprintf (sptr, zptr-sptr, ",\"deltaCGIplusRTEreused\":%d", SystemData.DeltaCgiPlusReused + SystemData.DeltaRteReused); if (SystemData.DeltaDclCrePrc) sptr += snprintf (sptr, zptr-sptr, ",\"deltaCREPRC\":%d", SystemData.DeltaDclCrePrc); if (SystemData.DeltaDclDelPrc) sptr += snprintf (sptr, zptr-sptr, ",\"deltaDELPRC\":%d", SystemData.DeltaDclDelPrc); /**************/ /* proxy data */ /**************/ if (SystemData.ProxyEnabled) { if (SystemData.ProxyDeltaTotal) sptr += snprintf (sptr, zptr-sptr, ",\"deltaProxy\":%d", SystemData.ProxyDeltaTotal); } sptr += snprintf (sptr, zptr-sptr, "}"); if (sptr >= zptr) EXIT_FI_LI (SS$_RESULTOVF); SystemData.SummaryBufferLength = sptr - SystemData.SummaryBuffer; } /*****************************************************************************/ /* Create a JSON data structure in a buffer containing the detailed server data. */ void JsonDetailData () { char *cptr, *sptr, *zptr; /*********/ /* begin */ /*********/ if (dbug) fprintf (stdout, "JsonDetailData()\n"); zptr = (sptr = SystemData.DetailBuffer) + sizeof(SystemData.DetailBuffer); sptr += snprintf (sptr, zptr-sptr, "{\"$data\":\"detail\""); if (SystemData.TooBusy) sptr += snprintf (sptr, zptr-sptr, ",\"tooBusy\":%d", SystemData.TooBusy); if (SystemData.MethodConnect) sptr += snprintf (sptr, zptr-sptr, ",\"methConnect\":%d", SystemData.MethodConnect); if (SystemData.MethodCopy) sptr += snprintf (sptr, zptr-sptr, ",\"methCopy\":%d", SystemData.MethodCopy); if (SystemData.MethodDelete) sptr += snprintf (sptr, zptr-sptr, ",\"methDelete\":%d", SystemData.MethodDelete); if (SystemData.MethodGet) sptr += snprintf (sptr, zptr-sptr, ",\"methGet\":%d", SystemData.MethodGet); if (SystemData.MethodHead) sptr += snprintf (sptr, zptr-sptr, ",\"methHead\":%d", SystemData.MethodHead); if (SystemData.MethodLock) sptr += snprintf (sptr, zptr-sptr, ",\"methLock\":%d", SystemData.MethodLock); if (SystemData.MethodMkCol) sptr += snprintf (sptr, zptr-sptr, ",\"methMkCol\":%d", SystemData.MethodMkCol); if (SystemData.MethodMove) sptr += snprintf (sptr, zptr-sptr, ",\"methMove\":%d", SystemData.MethodMove); if (SystemData.MethodOptions) sptr += snprintf (sptr, zptr-sptr, ",\"methOptions\":%d", SystemData.MethodOptions); if (SystemData.MethodPost) sptr += snprintf (sptr, zptr-sptr, ",\"methPost\":%d", SystemData.MethodPost); if (SystemData.MethodPropFind) sptr += snprintf (sptr, zptr-sptr, ",\"methPropFind\":%d", SystemData.MethodPropFind); if (SystemData.MethodPropPatch) sptr += snprintf (sptr, zptr-sptr, ",\"methPropPatch\":%d", SystemData.MethodPropPatch); if (SystemData.MethodPut) sptr += snprintf (sptr, zptr-sptr, ",\"methPut\":%d", SystemData.MethodPut); if (SystemData.MethodSsh) sptr += snprintf (sptr, zptr-sptr, ",\"methSsh\":%d", SystemData.MethodSsh); if (SystemData.MethodTrace) sptr += snprintf (sptr, zptr-sptr, ",\"methTrace\":%d", SystemData.MethodTrace); if (SystemData.MethodUnLock) sptr += snprintf (sptr, zptr-sptr, ",\"methUnLock\":%d", SystemData.MethodUnLock); if (SystemData.TotalAdmin) sptr += snprintf (sptr, zptr-sptr, ",\"doAdmin\":%d", SystemData.TotalAdmin); if (SystemData.TotalDclScript) sptr += snprintf (sptr, zptr-sptr, ",\"doDclScript\":%d", SystemData.TotalDclScript); if (SystemData.TotalDECnet) sptr += snprintf (sptr, zptr-sptr, ",\"doDECnet\":%d", SystemData.TotalDECnet); if (SystemData.TotalDir) sptr += snprintf (sptr, zptr-sptr, ",\"doDir\":%d", SystemData.TotalDir); if (SystemData.TotalFile) sptr += snprintf (sptr, zptr-sptr, ",\"doFile\":%d", SystemData.TotalFile); if (SystemData.TotalNoModule) sptr += snprintf (sptr, zptr-sptr, ",\"doNoModule\":%d", SystemData.TotalNoModule); if (SystemData.TotalAllOther) sptr += snprintf (sptr, zptr-sptr, ",\"doOther\":%d", SystemData.TotalAllOther); if (SystemData.TotalProxy) sptr += snprintf (sptr, zptr-sptr, ",\"doProxy\":%d", SystemData.TotalProxy); if (SystemData.TotalPut) sptr += snprintf (sptr, zptr-sptr, ",\"doPut\":%d", SystemData.TotalPut); if (SystemData.TotalSsi) sptr += snprintf (sptr, zptr-sptr, ",\"doSSI\":%d", SystemData.TotalSsi); if (SystemData.TotalUpdate) sptr += snprintf (sptr, zptr-sptr, ",\"doUpdate\":%d", SystemData.TotalUpdate); if (SystemData.TotalWebDav) sptr += snprintf (sptr, zptr-sptr, ",\"doWebDAV\":%d", SystemData.TotalWebDav); if (SystemData.NetReadErr) sptr += snprintf (sptr, zptr-sptr, ",\"errRx\":%d", SystemData.NetReadErr); if (SystemData.NetWriteErr) sptr += snprintf (sptr, zptr-sptr, ",\"errTx\":%d", SystemData.NetWriteErr); sptr += snprintf (sptr, zptr-sptr, ",\"bpsAve\":%d", SystemData.BytesPerSecAve); sptr += snprintf (sptr, zptr-sptr, ",\"bpsMax\":%d", SystemData.BytesPerSecMax); sptr += snprintf (sptr, zptr-sptr, ",\"bpsMin\":%d", SystemData.BytesPerSecMin); sptr += snprintf (sptr, zptr-sptr, "}"); if (sptr >= zptr) EXIT_FI_LI (SS$_RESULTOVF); SystemData.DetailBufferLength = sptr - SystemData.DetailBuffer; } /*****************************************************************************/ /* Create a JSON data structure in a buffer representing the proxy data. */ void JsonProxyData () { char *cptr, *sptr, *zptr; /*********/ /* begin */ /*********/ if (dbug) fprintf (stdout, "JsonProxyData()\n"); SystemData.ProxyBuffer[SystemData.ProxyBufferLength = 0] = '\0'; zptr = (sptr = SystemData.ProxyBuffer) + sizeof(SystemData.ProxyBuffer); if (!SystemData.ProxyEnabled) return; sptr += snprintf (sptr, zptr-sptr, "{\"$data\":\"proxy\""); if (SystemData.ProxyTotal) sptr += snprintf (sptr, zptr-sptr, ",\"total\":%d", SystemData.ProxyTotal); if (SystemData.ProxyBytesRawRx64) sptr += snprintf (sptr, zptr-sptr, ",\"networkRx\":%lld", SystemData.ProxyBytesRawRx64); if (SystemData.ProxyBytesRawTx64) sptr += snprintf (sptr, zptr-sptr, ",\"networkTx\":%lld", SystemData.ProxyBytesRawTx64); if (SystemData.ProxyLookupCache) sptr += snprintf (sptr, zptr-sptr, ",\"lookupCache\":%d", SystemData.ProxyLookupCache); if (SystemData.ProxyLookupDNS) sptr += snprintf (sptr, zptr-sptr, ",\"lookupDNS\":%d", SystemData.ProxyLookupDNS); if (SystemData.ProxyLookupLiteral) sptr += snprintf (sptr, zptr-sptr, ",\"lookupLiteral\":%d", SystemData.ProxyLookupLiteral); if (SystemData.ProxyLookupError) sptr += snprintf (sptr, zptr-sptr, ",\"lookupError\":%d", SystemData.ProxyLookupError); if (SystemData.ProxyNetwork) sptr += snprintf (sptr, zptr-sptr, ",\"network\":%d", SystemData.ProxyNetwork); if (SystemData.ProxyTunnel) sptr += snprintf (sptr, zptr-sptr, ",\"tunnel\":%d", SystemData.ProxyTunnel); if (SystemData.ProxyTunnelCurrent) sptr += snprintf (sptr, zptr-sptr, ",\"tunnelNow\":%d", SystemData.ProxyTunnelCurrent); if (SystemData.ProxyReworkCount) sptr += snprintf (sptr, zptr-sptr, ",\"reworkCount\":%d", SystemData.ProxyReworkCount); if (SystemData.ProxyReworkNoType) sptr += snprintf (sptr, zptr-sptr, ",\"reworkNoType\":%d", SystemData.ProxyReworkNoType); if (SystemData.ProxyReworkReplaceCount) sptr += snprintf (sptr, zptr-sptr, ",\"reworkReplaceCount\":%d", SystemData.ProxyReworkReplaceCount); if (SystemData.ProxyReworkReplaceSearch) sptr += snprintf (sptr, zptr-sptr, ",\"reworkReplaceSearch\":%d", SystemData.ProxyReworkReplaceSearch); if (SystemData.ProxyReworkTooBig) sptr += snprintf (sptr, zptr-sptr, ",\"reworkTooBig\":%d", SystemData.ProxyReworkTooBig); if (SystemData.ProxySocks5Count) sptr += snprintf (sptr, zptr-sptr, ",\"socks5Count\":%d", SystemData.ProxySocks5Count); if (SystemData.ProxySocks5Success) sptr += snprintf (sptr, zptr-sptr, ",\"socks5Success\":%d", SystemData.ProxySocks5Success); if (SystemData.ProxySocks5Fail) sptr += snprintf (sptr, zptr-sptr, ",\"socks5Fail\":%d", SystemData.ProxySocks5Fail); sptr += snprintf (sptr, zptr-sptr, "}"); if (sptr >= zptr) EXIT_FI_LI (SS$_RESULTOVF); SystemData.ProxyBufferLength = sptr - SystemData.ProxyBuffer; } /*****************************************************************************/ /* Create a JSON data structure in a buffer representing the WebDAV data. */ void JsonWebDavData () { char *cptr, *sptr, *zptr; /*********/ /* begin */ /*********/ if (dbug) fprintf (stdout, "JsonWebDavData()\n"); SystemData.WebDavBuffer[SystemData.WebDavBufferLength = 0] = '\0'; zptr = (sptr = SystemData.WebDavBuffer) + sizeof(SystemData.WebDavBuffer); if (!SystemData.WebDavEnabled) return; sptr += snprintf (sptr, zptr-sptr, "{\"$data\":\"webdav\""); if (SystemData.TotalWebDav) sptr += snprintf (sptr, zptr-sptr, ",\"total\":%u", SystemData.TotalWebDav); if (SystemData.TotalWebDavRead) sptr += snprintf (sptr, zptr-sptr, ",\"totalRead\":%u", SystemData.TotalWebDavRead); if (SystemData.TotalWebDavWrite) sptr += snprintf (sptr, zptr-sptr, ",\"totalWrite\":%u", SystemData.TotalWebDavWrite); if (SystemData.TotalWebDavLock) sptr += snprintf (sptr, zptr-sptr, ",\"totalLock\":%u", SystemData.TotalWebDavLock); if (SystemData.TotalWebDavOther) sptr += snprintf (sptr, zptr-sptr, ",\"totalOther\":%u", SystemData.TotalWebDavOther); if (sptr > zptr) EXIT_FI_LI (SS$_RESULTOVF); if (SystemData.TotalWebDav) sptr += snprintf (sptr, zptr-sptr, ",\"module\":%d", SystemData.TotalWebDav); if (SystemData.WebDavRequestCount) sptr += snprintf (sptr, zptr-sptr, ",\"request\":%u", SystemData.WebDavRequestCount); if (SystemData.WebDavMetaReadAttemptCount) sptr += snprintf (sptr, zptr-sptr, ",\"metaReadAttempt\":%u", SystemData.WebDavMetaReadAttemptCount); if (SystemData.WebDavMetaReadCount) sptr += snprintf (sptr, zptr-sptr, ",\"metaRead\":%u", SystemData.WebDavMetaReadCount); if (SystemData.WebDavMetaWriteAttemptCount) sptr += snprintf (sptr, zptr-sptr, ",\"metaWriteAttempt\":%u", SystemData.WebDavMetaWriteAttemptCount); if (SystemData.WebDavMetaWriteCount) sptr += snprintf (sptr, zptr-sptr, ",\"metaWrite\":%u", SystemData.WebDavMetaWriteCount); if (SystemData.WebDavXmlParseCount) sptr += snprintf (sptr, zptr-sptr, ",\"parseXML\":%u", SystemData.WebDavXmlParseCount); if (sptr > zptr) EXIT_FI_LI (SS$_RESULTOVF); sptr += snprintf (sptr, zptr-sptr, ",\"bytesRawRx\":%lld", SystemData.WebDavBytesRawRx64); sptr += snprintf (sptr, zptr-sptr, ",\"bytesRawTx\":%lld", SystemData.WebDavBytesRawTx64); sptr += snprintf (sptr, zptr-sptr, "}"); if (sptr >= zptr) EXIT_FI_LI (SS$_RESULTOVF); SystemData.WebDavBufferLength = sptr - SystemData.WebDavBuffer; } /*****************************************************************************/ /* Create a JSON data structure in a buffer representing the instance (server) process data. This is an array 0..n of instance processes. */ void JsonInstanceData () { static char CpuTime [32], UpTime [32]; static $DESCRIPTOR (CpuTimeFaoDsc, "!%T\0"); static $DESCRIPTOR (CpuTimeDsc, CpuTime); static $DESCRIPTOR (UpTimeFaoDsc, "!10%D\0"); static $DESCRIPTOR (UpTimeDsc, UpTime); static PROCESS_DATA ProcessData [INSTANCE_MAX]; int cnt, status; int64 ConTime64; char *cptr, *sptr, *zptr; PROCESS_DATA *pdptr; /*********/ /* begin */ /*********/ if (dbug) fprintf (stdout, "JsonInstanceData()\n"); zptr = (sptr = SystemData.InstanceBuffer) + sizeof(SystemData.InstanceBuffer); sptr += snprintf (sptr, zptr-sptr, "{\"$data\":\"instance\",\"instance\":["); for (cnt = 0; cnt < SystemData.InstanceNodeCount; cnt++) { pdptr = &ProcessData[cnt]; for (cptr = (cnt ? ",{" : "{"); *cptr; *sptr++ = *cptr++); sptr += snprintf (sptr, zptr-sptr, "\"pid\":\"%08.08X\"", SystemData.InstanceNodePid[cnt]); status = CollectProcessData (SystemData.InstanceNodePid[cnt], pdptr); if (VMSnok (status)) { sptr += snprintf (sptr, zptr-sptr, ",\"status\":%d}", status); continue; } sptr += snprintf (sptr, zptr-sptr, ",\"prcnam\":\"%s\"", pdptr->JpiPrcNam); if (sptr > zptr) EXIT_FI_LI (SS$_RESULTOVF); sptr += snprintf (sptr, zptr-sptr, ",\"astcnt\":%d", pdptr->JpiAstCnt); sptr += snprintf (sptr, zptr-sptr, ",\"astlm\":%d", pdptr->JpiAstLm); sptr += snprintf (sptr, zptr-sptr, ",\"biocnt\":%d", pdptr->JpiBioCnt); sptr += snprintf (sptr, zptr-sptr, ",\"biolm\":%d", pdptr->JpiBioLm); sptr += snprintf (sptr, zptr-sptr, ",\"bytcnt\":%d", pdptr->JpiBytCnt); sptr += snprintf (sptr, zptr-sptr, ",\"bufio\":%d", pdptr->JpiBufIo); sptr += snprintf (sptr, zptr-sptr, ",\"bytlm\":%d", pdptr->JpiBytLm); if (pdptr->JpiCpuId >= 0) sptr += snprintf (sptr, zptr-sptr, ",\"cpuid\":%d", pdptr->JpiCpuId); sptr += snprintf (sptr, zptr-sptr, ",\"diocnt\":%d", pdptr->JpiDioCnt); sptr += snprintf (sptr, zptr-sptr, ",\"dirio\":%d", pdptr->JpiDirIo); sptr += snprintf (sptr, zptr-sptr, ",\"diolm\":%d", pdptr->JpiDioLm); sptr += snprintf (sptr, zptr-sptr, ",\"enqcnt\":%d", pdptr->JpiEnqCnt); sptr += snprintf (sptr, zptr-sptr, ",\"enqlm\":%d", pdptr->JpiEnqLm); sptr += snprintf (sptr, zptr-sptr, ",\"filcnt\":%d", pdptr->JpiFilCnt); sptr += snprintf (sptr, zptr-sptr, ",\"fillm\":%d", pdptr->JpiFilLm); if (sptr > zptr) EXIT_FI_LI (SS$_RESULTOVF); sptr += snprintf (sptr, zptr-sptr, ",\"gpgcnt\":%d", pdptr->JpiGpgCnt); sptr += snprintf (sptr, zptr-sptr, ",\"ktcount\":%d", pdptr->JpiKtCount); if (pdptr->JpiMultiThread) sptr += snprintf (sptr, zptr-sptr, ",\"multithread\":%d", pdptr->JpiMultiThread); sptr += snprintf (sptr, zptr-sptr, ",\"pagfilcnt\":%d", pdptr->JpiPagFilCnt); sptr += snprintf (sptr, zptr-sptr, ",\"pageflts\":%d", pdptr->JpiPageFlts); sptr += snprintf (sptr, zptr-sptr, ",\"pgflquota\":%d", pdptr->JpiPgFlQuota); sptr += snprintf (sptr, zptr-sptr, ",\"ppgcnt\":%d", pdptr->JpiPpgCnt); sptr += snprintf (sptr, zptr-sptr, ",\"prccnt\":%d", pdptr->JpiPrcCnt); sptr += snprintf (sptr, zptr-sptr, ",\"prclm\":%d", pdptr->JpiPrcLm); if (sptr > zptr) EXIT_FI_LI (SS$_RESULTOVF); sptr += snprintf (sptr, zptr-sptr, ",\"astFlags\":\"%s\"", pdptr->AstFlags); sptr += snprintf (sptr, zptr-sptr, ",\"pri\":%d", pdptr->JpiPri); sptr += snprintf (sptr, zptr-sptr, ",\"prib\":%d", pdptr->JpiPrib); sptr += snprintf (sptr, zptr-sptr, ",\"state\":\"%s\"", pdptr->ProcessState); sptr += snprintf (sptr, zptr-sptr, ",\"tqlm\":%d", pdptr->JpiTqLm); sptr += snprintf (sptr, zptr-sptr, ",\"tqcnt\":%d", pdptr->JpiTqCnt); sptr += snprintf (sptr, zptr-sptr, ",\"wsextent\":%d", pdptr->JpiWsExtent); sptr += snprintf (sptr, zptr-sptr, ",\"wspeak\":%d", pdptr->JpiWsPeak); sptr += snprintf (sptr, zptr-sptr, ",\"wsquota\":%d", pdptr->JpiWsQuota); sptr += snprintf (sptr, zptr-sptr, ",\"virtpeak\":%d", pdptr->JpiVirtPeak64); if (pdptr->JpiVolumes) sptr += snprintf (sptr, zptr-sptr, ",\"volumes\":%d", pdptr->JpiVolumes); if (pdptr->PageFaults) sptr += snprintf (sptr, zptr-sptr, ",\"pageFaults\":%d", pdptr->PageFaults); sys$fao (&CpuTimeFaoDsc, 0, &CpuTimeDsc, &pdptr->CpuTime64); for (cptr = CpuTime; *cptr && (*cptr == ' ' || *cptr == ':' || *cptr == '0') && (*cptr+1) != '.'; cptr++); sptr += snprintf (sptr, zptr-sptr, ",\"cpuTime\":\"%s\"", cptr); sys$fao (&UpTimeFaoDsc, 0, &UpTimeDsc, &pdptr->ConTime64); for (cptr = UpTime; *cptr && *cptr == ' '; cptr++); sptr += snprintf (sptr, zptr-sptr, ",\"conTime\":\"%s\"", cptr); sptr += snprintf (sptr, zptr-sptr, "}"); } sptr += snprintf (sptr, zptr-sptr, "]}"); if (sptr >= zptr) EXIT_FI_LI (SS$_RESULTOVF); SystemData.InstanceBufferLength = sptr - SystemData.InstanceBuffer; } /*****************************************************************************/ /* Create a JSON data structure in a buffer representing the instance report. See [SRC.HTTPD]INSTANCE.C InstanceCliReport(). */ void JsonInstanceStatus (BOOL WorldAccess) { static ulong LibDeltaMins = LIB$K_DELTA_MINUTES; BOOL stale; int cnt, idx, len, maxtab, status, total; int64 AgoTime64; ulong MinsAgo; char *c1ptr, *c2ptr, *sptr, *zptr; char ExitAgoBuf [16], ExitStatusBuf [16], StartAgoBuf [16], TimeExit [32], TimeStartup [32], UpdateAgoBuf [16]; INSTANCE_STATUS *istptr; /*********/ /* begin */ /*********/ if (dbug) fprintf (stdout, "JsonInstanceStatus()\n"); maxtab = accptr->InstanceStatusTableCount; zptr = (sptr = SystemData.StatusBuffer) + sizeof(SystemData.StatusBuffer); sptr += snprintf (sptr, zptr-sptr, "{\"$data\":\"status\",\"instance\":["); for (idx = cnt = total = 0; idx < maxtab; idx++) { istptr = &accptr->InstanceStatusTable[idx]; /* if a purged entry */ if (!istptr->UpdateTime64) continue; if (WorldAccess) { /* only instances on this node */ c1ptr = istptr->InstanceName; c2ptr = SystemData.NodeName; while (*c1ptr && *c1ptr != ':' && *c2ptr && *c1ptr == *c2ptr) { c1ptr++; c2ptr++; } if (*c1ptr != ':' || *c2ptr) continue; } TimeSansYear (&istptr->StartTime64, TimeStartup); TimeSansYear (&istptr->ExitTime64, TimeExit); ThisLongAgo (&istptr->ExitTime64, ExitAgoBuf); ThisLongAgo (&istptr->StartTime64, StartAgoBuf); ThisLongAgo (&istptr->UpdateTime64, UpdateAgoBuf); if (istptr->ExitStatus) sprintf (ExitStatusBuf, "%%X%08.08X", istptr->ExitStatus); else ExitStatusBuf[0] = '\0'; status = lib$sub_times (&CurrentTime64, &istptr->UpdateTime64, &AgoTime64); if (VMSok (status)) status = lib$cvt_from_internal_time (&LibDeltaMins, &MinsAgo, &AgoTime64); if (VMSok(status) && MinsAgo > INSTANCE_STATUS_STALE_MINS) stale = true; else stale = false; sptr += snprintf (sptr, zptr-sptr, "%s[%u,\"%s\",\"%s\",\"%s\",\"%s\",%u,\"%s\",\"%s\",\"%s\",\"%s\",%u,%u]", cnt++ ? "," : "", stale ? 0 : ++total, istptr->InstanceName, UpdateAgoBuf, TimeStartup, StartAgoBuf, istptr->StartupCount, TimeExit, ExitAgoBuf, ExitStatusBuf, istptr->HttpdVersion, istptr->MinuteCount, istptr->HourCount); } sptr += snprintf (sptr, zptr-sptr, "]}"); if (sptr >= zptr) EXIT_FI_LI (SS$_RESULTOVF); SystemData.StatusBufferLength = sptr - SystemData.StatusBuffer; } /*****************************************************************************/ /* Put into the supplied buffer the time with the year removed (e.g. 31-OCT 23:01:15"). The buffer should have at least 16 bytes capacity. */ void TimeSansYear ( ulong *Time64Ptr, char *Buffer ) { static $DESCRIPTOR (TimeFaoDsc, "!20%D\0"); static char TimeBuf [32]; static $DESCRIPTOR (TimeBufDsc, TimeBuf); int status; char *cptr, *sptr; /*********/ /* begin */ /*********/ if (dbug) fprintf (stdout, "TimeSansYear()\n"); if (!*Time64Ptr) { strcpy (Buffer, "(none)"); return; } Buffer[0] = '\0'; status = sys$fao (&TimeFaoDsc, 0, &TimeBufDsc, Time64Ptr); if (VMSnok (status)) return; sptr = Buffer; for (cptr = TimeBuf; *cptr && *cptr != '-'; *sptr++ = *cptr++); for (*sptr++ = *cptr++; *cptr && *cptr != '-'; *sptr++ = *cptr++); while (*cptr && *cptr != ' ') cptr++; while (*cptr) *sptr++ = *cptr++; *sptr = '\0'; if (Buffer[0] == ' ') Buffer[0] = '0'; } /*****************************************************************************/ /* Format a string containing how many seconds, minutes, hours, days ago from the current time. For example; "15s", "10m", "17h", "152d". Return VMS status. |AgoBuf| should be at least 16 chars. */ void ThisLongAgo ( int64 *Time64Ptr, char *AgoBuf ) { static ulong LibDeltaSecs = LIB$K_DELTA_SECONDS; static $DESCRIPTOR (AgoFaoDsc, "!UL!AZ\0"); static $DESCRIPTOR (AgoBufDsc, ""); int status; ulong SecsAgo; int64 NowTime64; /*********/ /* begin */ /*********/ *(USHORTPTR)AgoBuf = '0\0'; if (!*Time64Ptr) return; sys$gettim (&NowTime64); SecsAgo = (NowTime64 - *Time64Ptr) / TIME64_ONE_SEC; AgoBufDsc.dsc$a_pointer = AgoBuf; AgoBufDsc.dsc$w_length = 16; status = SS$_NORMAL; if (SecsAgo >= (60*60*24)) status = sys$fao (&AgoFaoDsc, 0, &AgoBufDsc, SecsAgo / (60*60*24), "d"); else if (SecsAgo >= (60*60)) status = sys$fao (&AgoFaoDsc, 0, &AgoBufDsc, SecsAgo / (60*60), "h"); else if (SecsAgo >= 60) status = sys$fao (&AgoFaoDsc, 0, &AgoBufDsc, SecsAgo / 60, "m"); else if (SecsAgo > 1) status = sys$fao (&AgoFaoDsc, 0, &AgoBufDsc, SecsAgo, "s"); else strcpy (AgoBuf, "now"); } /*****************************************************************************/ /* Create a JSON data structure in a buffer representing the last request data. */ void JsonRequestData ( CLIENT_DATA *clptr, BOOL ForceUpdate ) { static char LastDuration[sizeof(gblptr->Request.Time)], LastTime[sizeof(gblptr->Request.Time)]; int cnt, status; char *cptr, *sptr, *zptr; char *geoptr = NULL; PROCESS_DATA ProcessData; PROCESS_DATA *pdptr = &ProcessData; /*********/ /* begin */ /*********/ if (dbug) fprintf (stdout, "JsonRequestData()\n"); if (!ForceUpdate && !strcmp (LastTime, gblptr->Request.Time) && !strcmp (LastDuration, gblptr->Request.Duration)) { /* only provide request data when it has changed */ SystemData.RequestBuffer[SystemData.RequestBufferLength = 0] = '\0'; return; } #ifdef ALAMODE_GEOLOCATE if (GeoLocatePtr && GeoLocatePtr[0] && GeoLocatePtr[0] != '!') { geoptr = HttpdGblSecPtr->Request.ClientIpAddressString; if (isalnum(GeoLocatePtr[0])) geoptr = GeoLocate (GeoLocatePtr, geoptr); else if (isalnum(GeoLocatePtr[1])) geoptr = GeoLocate (GeoLocatePtr+1, geoptr); else geoptr = GeoLocate (NULL, geoptr); if (!geoptr) geoptr = "[null]"; else if (*geoptr) geoptr = GeoLocateData (GeoLocatePtr[0] == '?'); else if (*(geoptr+1)) geoptr++; else geoptr = "[oops]"; } #endif /* ALAMODE_GEOLOCATE */ strcpy (LastTime, gblptr->Request.Time); strcpy (LastDuration, gblptr->Request.Duration); if (TcpIpIsAddress (cptr = gblptr->Request.ClientHostName)) cptr = TcpIpLookup (NULL, gblptr->Request.ClientHostName, NULL, NULL); SystemData.RequestBuffer[SystemData.RequestBufferLength = 0] = '\0'; zptr = (sptr = SystemData.RequestBuffer) + sizeof(SystemData.RequestBuffer); sptr += snprintf (sptr, zptr-sptr, "{\"$data\":\"request\""); sptr += snprintf (sptr, zptr-sptr, ",\"alert\":%s", gblptr->Request.Alert ? "true" : "false"); sptr += snprintf (sptr, zptr-sptr, ",\"time\":\"%s\"", gblptr->Request.Time); sptr += snprintf (sptr, zptr-sptr, ",\"client\":\"%s\"", cptr); sptr += snprintf (sptr, zptr-sptr, ",\"clientIP\":\"%s\"", gblptr->Request.ClientIpAddressString); if (geoptr) sptr += snprintf (sptr, zptr-sptr, ",\"geolocate\":\"{%s}\"", geoptr); sptr += snprintf (sptr, zptr-sptr, ",\"service\":\"%s\"", gblptr->Request.Service); sptr += snprintf (sptr, zptr-sptr, ",\"prcnam\":\"%s\"", gblptr->Request.PrcNam); sptr += snprintf (sptr, zptr-sptr, ",\"method\":\"%s\"", gblptr->Request.MethodName); /* URI has potential to have unescaped reserved characters */ for (cptr = ",\"uri\":\""; *cptr; *sptr++ = *cptr++); if (clptr && clptr->WorldAccess && gblptr->Request.AuthUser[0]) for (cptr = gblptr->Request.Uri; *cptr && sptr < zptr; cptr++) *sptr++ = '*'; else for (cptr = gblptr->Request.Uri; *cptr && sptr < zptr; *sptr++ = *cptr++) if (*cptr == '\"' || *cptr == '\\') *sptr++ = '\\'; if (cptr - gblptr->Request.Uri == sizeof(gblptr->Request.Uri)-1) for (cptr = "..."; *cptr; *sptr++ = *cptr++); *sptr++ = '\"'; switch (gblptr->Request.HttpProtocol) { case HTTP_VERSION_2 : cptr = "2"; break; case HTTP_VERSION_1_1 : cptr = "1.1"; break; case HTTP_VERSION_1_0 : cptr = "1.0"; break; case HTTP_VERSION_0_9 : cptr = "0.9"; break; default : cptr = "?"; } sptr += snprintf (sptr, zptr-sptr, ",\"http\":\"%s\"", cptr); sptr += snprintf (sptr, zptr-sptr, ",\"status\":%d", gblptr->Request.HttpStatus); sptr += snprintf (sptr, zptr-sptr, ",\"rx\":%lld", gblptr->Request.BytesRawRx64); sptr += snprintf (sptr, zptr-sptr, ",\"tx\":%lld", gblptr->Request.BytesRawTx64); sptr += snprintf (sptr, zptr-sptr, ",\"bps\":%d", gblptr->Request.BytesPerSecond); sptr += snprintf (sptr, zptr-sptr, ",\"gzipTx\":%d", gblptr->Request.BytesTxGzipPercent); sptr += snprintf (sptr, zptr-sptr, ",\"duration\":\"%s\"", gblptr->Request.Duration); if (gblptr->Request.AuthUser[0]) if (clptr && clptr->WorldAccess) sptr += snprintf (sptr, zptr-sptr, ",\"user\":\"********\""); else sptr += snprintf (sptr, zptr-sptr, ",\"user\":\"%s\"", gblptr->Request.AuthUser); sptr += snprintf (sptr, zptr-sptr, "}"); if (sptr >= zptr) EXIT_FI_LI (SS$_RESULTOVF); SystemData.RequestBufferLength = sptr - SystemData.RequestBuffer; } /*****************************************************************************/ /* Create a JSON data structure in a buffer representing the scripting data. */ void JsonScriptData () { char *cptr, *sptr, *zptr; /*********/ /* begin */ /*********/ if (dbug) fprintf (stdout, "JsonScriptData()\n"); SystemData.ScriptBuffer[SystemData.ScriptBufferLength = 0] = '\0'; zptr = (sptr = SystemData.ScriptBuffer) + sizeof(SystemData.ScriptBuffer); sptr += snprintf (sptr, zptr-sptr, "{\"$data\":\"script\""); if (SystemData.CurrentScript) sptr += snprintf (sptr, zptr-sptr, ",\"scriptNow\":%d", SystemData.CurrentScript); if (SystemData.CurrentScriptCGIplus) sptr += snprintf (sptr, zptr-sptr, ",\"scriptCGIplusNow\":%d", SystemData.CurrentScriptCGIplus); if (SystemData.CurrentScriptRTE) sptr += snprintf (sptr, zptr-sptr, ",\"scriptRTENow\":%d", SystemData.CurrentScriptRTE); if (SystemData.TotalDclHardLimit) sptr += snprintf (sptr, zptr-sptr, ",\"scriptHardLimit\":%d", SystemData.TotalDclHardLimit); if (SystemData.TotalCgi) sptr += snprintf (sptr, zptr-sptr, ",\"totalCGI\":%d", SystemData.TotalCgi); if (SystemData.TotalDclCommand) sptr += snprintf (sptr, zptr-sptr, ",\"totalCLI\":%d", SystemData.TotalDclCommand); if (SystemData.TotalCgiPlus) sptr += snprintf (sptr, zptr-sptr, ",\"totalCGIplus\":%d", SystemData.TotalCgiPlus); if (SystemData.TotalCgiPlus) sptr += snprintf (sptr, zptr-sptr, ",\"totalCGIplusReused\":%d", SystemData.TotalCgiPlusReused); if (SystemData.TotalRte) sptr += snprintf (sptr, zptr-sptr, ",\"totalRTE\":%d", SystemData.TotalRte); if (SystemData.TotalRteReused) sptr += snprintf (sptr, zptr-sptr, ",\"totalRTEreused\":%d", SystemData.TotalRteReused); if (SystemData.CurrentDECnetTasks) sptr += snprintf (sptr, zptr-sptr, ",\"DECnetNow\":%d", SystemData.CurrentDECnetTasks); if (SystemData.CurrentDECnetCGI) sptr += snprintf (sptr, zptr-sptr, ",\"DECnetCGINow\":%d", SystemData.CurrentDECnetCGI); if (SystemData.CurrentDECnetOSU) sptr += snprintf (sptr, zptr-sptr, ",\"DECnetOSUNow\":%d", SystemData.CurrentDECnetOSU); if (SystemData.TotalDECnetCgi) sptr += snprintf (sptr, zptr-sptr, ",\"totalDECnetCGI\":%d", SystemData.TotalDECnetCgi); if (SystemData.TotalDECnetCgiReused) sptr += snprintf (sptr, zptr-sptr, ",\"totalDECnetCGIreused\":%d", SystemData.TotalDECnetCgiReused); if (SystemData.TotalDECnetOsu) sptr += snprintf (sptr, zptr-sptr, ",\"totalDECnetOSU\":%d", SystemData.TotalDECnetOsu); if (SystemData.TotalDECnetOsuReused) sptr += snprintf (sptr, zptr-sptr, ",\"totalDECnetOSUreused\":%d", SystemData.TotalDECnetOsuReused); sptr += snprintf (sptr, zptr-sptr, "}"); if (sptr >= zptr) EXIT_FI_LI (SS$_RESULTOVF); SystemData.ScriptBufferLength = sptr - SystemData.ScriptBuffer; } /*****************************************************************************/ /* Server process accounting data. */ void CollectAccounting () { static ulong MemSize, PageSize, PrevFileCount, PrevHttpCount, PrevHttp1Count, PrevHttp2Count, PrevOtherCount, PrevProxyNetwork, PrevProxyTotal, PrevRequestAllCount, PrevRequestFailCount, PrevScriptCgiCount, PrevThrottleBusy, PrevThrottled, PrevThrottleQueued, PrevTotalScript, PrevWebDavRead, PrevWebDavTotal, PrevWebDavWrite; static int64 BytesRawRxTx64, PrevBlocksRawRx64, PrevBlocksRawTx64, PrevBytesRawRx64, PrevBytesRawTx64, PrevBytesRawRxTx64; int idx, status, total; int64 quad, BlocksRawRx64, BlocksRawTx64, BytesRawRx64, BytesRawTx64; ulong TotalScript, RequestAllCount, RequestFailCount, ScriptCgiCount; char *cptr, *sptr, *zptr; /*********/ /* begin */ /*********/ if (dbug) fprintf (stdout, "CollectAccounting()\n"); SystemData.ConnectSuspend = gblptr->ConnectSuspend; SystemData.InstancePassive = gblptr->InstancePassive; SystemData.InstanceNodeSuperPid = gblptr->HttpdProcessId; SystemData.InstanceStartupCount = accptr->StartupCount; SystemData.CurrentConnected = accptr->CurrentConnected[HTTP12]; SystemData.CurrentHttp1Connected = accptr->CurrentConnected[HTTP1]; SystemData.CurrentHttp2Connected = accptr->CurrentConnected[HTTP2]; SystemData.ConnectTotal = accptr->ConnectCount[HTTP12]; SystemData.ConnectHttp1Total = accptr->ConnectCount[HTTP1]; SystemData.ConnectHttp2Total = accptr->ConnectCount[HTTP2]; SystemData.ConnectPeak = accptr->ConnectPeak[HTTP12]; SystemData.ConnectHttp1Peak = accptr->ConnectPeak[HTTP1]; SystemData.ConnectHttp2Peak = accptr->ConnectPeak[HTTP2]; SystemData.CurrentProcessing = accptr->CurrentProcessing[HTTP12]; SystemData.CurrentHttp1Processing = accptr->CurrentProcessing[HTTP1]; SystemData.CurrentHttp2Processing = accptr->CurrentProcessing[HTTP2]; SystemData.ProcessingTotal = accptr->ProcessingTotalCount[HTTP12]; SystemData.ProcessingHttp1Total = accptr->ProcessingTotalCount[HTTP1]; SystemData.ProcessingHttp2Total = accptr->ProcessingTotalCount[HTTP2]; SystemData.ProcessingPeak = accptr->ProcessingPeak[HTTP12]; SystemData.ProcessingHttp1Peak = accptr->ProcessingPeak[HTTP1]; SystemData.ProcessingHttp2Peak = accptr->ProcessingPeak[HTTP2]; SystemData.SslCount = accptr->ConnectSSLCount; SystemData.TooBusy = accptr->ConnectTooBusyCount; for (total = idx = 0; idx <= gblptr->InstanceNodeCurrent; idx++) total += accptr->CurrentWebSockets[idx]; SystemData.CurrentWebSockets = total; /****************/ /* HTTP methods */ /****************/ SystemData.MethodConnect = accptr->MethodConnectCount; SystemData.MethodCopy = accptr->MethodWebDavCopyCount; SystemData.MethodDelete = accptr->MethodDeleteCount; SystemData.MethodGet = accptr->MethodGetCount; SystemData.MethodHead = accptr->MethodHeadCount; SystemData.MethodLock = accptr->MethodWebDavLockCount; SystemData.MethodMkCol = accptr->MethodWebDavMkColCount; SystemData.MethodMove = accptr->MethodWebDavMoveCount; SystemData.MethodPropFind = accptr->MethodWebDavPropFindCount; SystemData.MethodPropPatch = accptr->MethodWebDavPropPatchCount; SystemData.MethodOptions = accptr->MethodOptionsCount; SystemData.MethodPost = accptr->MethodPostCount; SystemData.MethodPut = accptr->MethodPutCount; SystemData.MethodSsh = accptr->MethodSshCount; SystemData.MethodTrace = accptr->MethodTraceCount; SystemData.MethodUnLock = accptr->MethodWebDavUnLockCount; /*******************/ /* response status */ /*******************/ SystemData.StatusCodeGroup[0] = accptr->ResponseStatusCodeGroup[0]; SystemData.StatusCodeGroup[1] = accptr->ResponseStatusCodeGroup[1]; SystemData.StatusCodeGroup[2] = accptr->ResponseStatusCodeGroup[2]; SystemData.StatusCodeGroup[3] = accptr->ResponseStatusCodeGroup[3]; SystemData.StatusCodeGroup[4] = accptr->ResponseStatusCodeGroup[4]; SystemData.StatusCodeGroup[5] = accptr->ResponseStatusCodeGroup[5]; SystemData.StatusCode403 = accptr->RequestForbiddenCount; RequestAllCount = SystemData.StatusCodeGroup[0] + SystemData.StatusCodeGroup[1] + SystemData.StatusCodeGroup[2] + SystemData.StatusCodeGroup[3] + SystemData.StatusCodeGroup[4] + SystemData.StatusCodeGroup[5]; RequestFailCount = SystemData.StatusCodeGroup[0] + SystemData.StatusCodeGroup[4] + SystemData.StatusCodeGroup[5]; if (AlertStatusCodeCount) /* alerts for ALAMODE_ALERT_NNN defined HTTP statuses */ for (idx = 0; idx < AlertStatusCodeCount; idx++) SystemData.AlertStatusCode[idx].current = HttpStatusCount (SystemData.AlertStatusCode[idx].code); /** SystemData.ProcessingTotal = accptr->ProcessingTotalCount[HTTP12]; **/ if (PrevRequestAllCount && RequestAllCount > PrevRequestAllCount) SystemData.DeltaRequest = RequestAllCount - PrevRequestAllCount; else SystemData.DeltaRequest = 0; PrevRequestAllCount = RequestAllCount; if (PrevRequestFailCount && RequestFailCount > PrevRequestFailCount) SystemData.DeltaRequestFail = RequestFailCount - PrevRequestFailCount; else SystemData.DeltaRequestFail = 0; PrevRequestFailCount = RequestFailCount; /***************/ /* bytes rx/tx */ /***************/ SystemData.BlocksRawRx64 = accptr->BlocksRawRx64[HTTP12]; SystemData.BlocksRawTx64 = accptr->BlocksRawTx64[HTTP12]; SystemData.BytesRawRx64 = accptr->BytesRawRx64[HTTP12]; SystemData.BytesRawTx64 = accptr->BytesRawTx64[HTTP12]; BytesRawRxTx64 = SystemData.BytesRawRx64; BytesRawRxTx64 += SystemData.BytesRawTx64; BytesRawRxTx64 += SystemData.ProxyBytesRawRx64; BytesRawRxTx64 += SystemData.ProxyBytesRawTx64; quad = BytesRawRxTx64; quad -= PrevBytesRawRxTx64; PrevBytesRawRxTx64 = BytesRawRxTx64; SystemData.BytesRawPerSec = (ulong)(float)quad / (float)DeltaSeconds; BytesRawRx64 = SystemData.BytesRawRx64; BytesRawRx64 = PrevBytesRawRx64; PrevBytesRawRx64 = SystemData.BytesRawRx64; BlocksRawRx64 = SystemData.BlocksRawRx64; BlocksRawRx64 -= PrevBlocksRawRx64; PrevBlocksRawRx64 = SystemData.BlocksRawRx64; if (BlocksRawRx64) SystemData.BlockReadSize = (ulong)(float)BytesRawRx64 / (float)BlocksRawRx64 / (float)DeltaSeconds; else SystemData.BlockReadSize = 0; BytesRawTx64 = SystemData.BytesRawTx64; BytesRawTx64 -= PrevBytesRawTx64; PrevBytesRawTx64 = SystemData.BytesRawTx64; BlocksRawTx64 = SystemData.BlocksRawTx64; BlocksRawTx64 -= PrevBlocksRawTx64; PrevBlocksRawTx64 = SystemData.BlocksRawTx64; if (BlocksRawTx64) SystemData.BlockWriteSize = (ulong)(float)BytesRawTx64 / (float)BlocksRawTx64 / (float)DeltaSeconds; else SystemData.BlockWriteSize = 0; SystemData.NetReadErr = accptr->NetReadErrorCount; SystemData.NetWriteErr = accptr->NetWriteErrorCount; SystemData.BytesPerSecAve = accptr->BytesPerSecondAve; SystemData.BytesPerSecMax = accptr->BytesPerSecondMax; SystemData.BytesPerSecMin = accptr->BytesPerSecondMin; /****************/ /* basic totals */ /****************/ SystemData.TotalAdmin = accptr->DoServerAdminCount; SystemData.TotalDECnet = accptr->DoDECnetCount; SystemData.TotalDir = accptr->DoDirectoryCount; SystemData.TotalFile = accptr->DoFileCount; SystemData.TotalProxy = accptr->DoProxyCount; SystemData.TotalPut = accptr->DoPutCount; SystemData.TotalSsi = accptr->DoSsiCount; SystemData.TotalUpdate = accptr->DoUpdateCount; SystemData.TotalWebSocket = accptr->DclWebSocketCount; /****************/ /* basic deltas */ /****************/ SystemData.DeltaHttp = SystemData.ProcessingTotal; if (PrevHttpCount && SystemData.ProcessingTotal > PrevHttpCount) SystemData.DeltaHttp -= PrevHttpCount; else SystemData.DeltaHttp = 0; PrevHttpCount = SystemData.ProcessingTotal; SystemData.DeltaHttp1 = SystemData.ProcessingHttp1Total; if (PrevHttp1Count && SystemData.ProcessingHttp1Total > PrevHttp1Count) SystemData.DeltaHttp1 -= PrevHttp1Count; else SystemData.DeltaHttp1 = 0; PrevHttp1Count = SystemData.ProcessingHttp1Total; SystemData.DeltaHttp2 = SystemData.ProcessingHttp2Total; if (PrevHttp2Count && SystemData.ProcessingHttp2Total > PrevHttp2Count) SystemData.DeltaHttp2 -= PrevHttp2Count; else SystemData.DeltaHttp2 = 0; PrevHttp2Count = SystemData.ProcessingHttp2Total; SystemData.DeltaFile = SystemData.TotalFile; if (PrevFileCount && SystemData.TotalFile > PrevFileCount) SystemData.DeltaFile -= PrevFileCount; else SystemData.DeltaFile = 0; PrevFileCount = SystemData.TotalFile; /* accptr->DoDclCommandCount is a special "script" for "internal" purposes */ TotalScript = accptr->DoDECnetCount + accptr->DoCgiPlusScriptCount + accptr->DoRteScriptCount + accptr->DoScriptCount; SystemData.TotalAllScript = TotalScript; if (PrevTotalScript && TotalScript > PrevTotalScript) SystemData.DeltaAllScript = TotalScript - PrevTotalScript; else SystemData.DeltaAllScript = 0; PrevTotalScript = TotalScript; SystemData.TotalDclScript = TotalScript - accptr->DoDECnetCount; ScriptCgiCount = accptr->DoDclCommandCount + accptr->DoDECnetCount + accptr->DoScriptCount; SystemData.DeltaScriptCgi = ScriptCgiCount; if (PrevScriptCgiCount && ScriptCgiCount > PrevScriptCgiCount) SystemData.DeltaScriptCgi = ScriptCgiCount - PrevScriptCgiCount; else SystemData.DeltaScriptCgi = 0; PrevScriptCgiCount = ScriptCgiCount; /*********/ /* proxy */ /*********/ if (SystemData.ProxyEnabled) { SystemData.ProxyTotal = accptr->DoProxyCount; SystemData.ProxyNetwork = pacptr->ConnectIpv4Count + pacptr->ConnectIpv6Count; if (PrevProxyTotal && SystemData.ProxyTotal > PrevProxyTotal) SystemData.ProxyDeltaTotal = SystemData.ProxyTotal - PrevProxyTotal; else SystemData.ProxyDeltaTotal = 0; PrevProxyTotal = SystemData.ProxyTotal; if (PrevProxyNetwork && SystemData.ProxyNetwork > PrevProxyNetwork) SystemData.ProxyDeltaNetwork = SystemData.ProxyNetwork - PrevProxyNetwork; else SystemData.ProxyDeltaNetwork = 0; PrevProxyNetwork = SystemData.ProxyNetwork; } /**********/ /* WebDAV */ /**********/ if (SystemData.WebDavEnabled) { SystemData.TotalWebDavRead = accptr->MethodWebDav_GetCount; SystemData.TotalWebDavWrite = accptr->MethodWebDavCopyCount + accptr->MethodWebDav_DeleteCount + accptr->MethodWebDavMkColCount + accptr->MethodWebDavMoveCount + accptr->MethodWebDav_PutCount; SystemData.TotalWebDavLock = accptr->MethodWebDavLockCount + accptr->MethodWebDavUnLockCount; SystemData.TotalWebDavOther = accptr->MethodWebDav_OptionsCount + accptr->MethodWebDavPropFindCount + accptr->MethodWebDavPropPatchCount; SystemData.TotalWebDav = SystemData.TotalWebDavLock + SystemData.TotalWebDavOther + SystemData.TotalWebDavRead + SystemData.TotalWebDavWrite; if (PrevWebDavTotal && SystemData.TotalWebDav > PrevWebDavTotal) SystemData.DeltaWebDavTotal = SystemData.TotalWebDav - PrevWebDavTotal; else SystemData.DeltaWebDavTotal = 0; PrevWebDavTotal = SystemData.TotalWebDav; if (PrevWebDavRead && SystemData.TotalWebDavRead > PrevWebDavRead) SystemData.DeltaWebDavRead = SystemData.TotalWebDavRead - PrevWebDavRead; else SystemData.DeltaWebDavRead = 0; PrevWebDavRead = SystemData.TotalWebDavRead; if (PrevWebDavWrite && SystemData.TotalWebDavWrite > PrevWebDavWrite) SystemData.DeltaWebDavWrite = SystemData.TotalWebDavWrite - PrevWebDavWrite; else SystemData.DeltaWebDavWrite = 0; PrevWebDavWrite = SystemData.TotalWebDavWrite; } /************/ /* throttle */ /************/ for (total = idx = 0; idx <= gblptr->InstanceNodeCurrent; idx++) total += accptr->CurrentThrottleProcessing[idx]; SystemData.CurrentThrottleProcessing = total; for (total = idx = 0; idx <= gblptr->InstanceNodeCurrent; idx++) total += accptr->CurrentThrottleQueued [idx]; SystemData.CurrentThrottleQueued = total; SystemData.CurrentThrottled = SystemData.CurrentThrottleProcessing + total; /* throttled is initially processing plus initially queued plus init busy */ SystemData.TotalThrottled = accptr->ThrottleTotalProcessed + accptr->ThrottleTotalQueued + accptr->ThrottleTotalBusy; if (PrevThrottled && SystemData.TotalThrottled > PrevThrottled) SystemData.DeltaThrottled = SystemData.TotalThrottled - PrevThrottled; else SystemData.DeltaThrottled = 0; PrevThrottled = SystemData.TotalThrottled; SystemData.TotalThrottleQueued = accptr->ThrottleTotalQueued; if (PrevThrottleQueued && SystemData.TotalThrottleQueued > PrevThrottleQueued) SystemData.DeltaThrottleQueued = SystemData.TotalThrottleQueued - PrevThrottleQueued; else SystemData.DeltaThrottleQueued = 0; PrevThrottleQueued = SystemData.TotalThrottleQueued; SystemData.CurrentThrottleBusyMetric = accptr->ThrottleBusyMetric; SystemData.TotalThrottleBusy = accptr->ThrottleTotalBusy; if (PrevThrottleBusy && SystemData.TotalThrottleBusy > PrevThrottleBusy) SystemData.DeltaThrottleBusy = SystemData.TotalThrottleBusy - PrevThrottleBusy; else SystemData.DeltaThrottleBusy = 0; PrevThrottleBusy = SystemData.TotalThrottleBusy; /********/ /* misc */ /********/ SystemData.DclProcessCurrent = SystemData.CurrentDECnetTasks = SystemData.CurrentDECnetCGI = SystemData.CurrentDECnetOSU = 0; for (idx = 0; idx <= gblptr->InstanceNodeCurrent; idx++) { SystemData.DclProcessCurrent += accptr->CurrentDclScriptProcess[idx]; SystemData.CurrentDECnetTasks += accptr->CurrentDECnetTasks[idx]; SystemData.CurrentDECnetCGI += accptr->CurrentDECnetCGI[idx]; SystemData.CurrentDECnetOSU += accptr->CurrentDECnetOSU[idx]; } SystemData.TotalNoModule = accptr->DoNoModuleCount; SystemData.TotalAllOther = SystemData.ProcessingTotal - SystemData.TotalAllScript - SystemData.TotalFile - SystemData.TotalProxy; if (PrevOtherCount && SystemData.TotalAllOther > PrevOtherCount) SystemData.DeltaOther = SystemData.TotalAllOther - PrevOtherCount; else SystemData.DeltaOther = 0; PrevOtherCount = SystemData.TotalAllOther; } /*****************************************************************************/ /* Collect proxy data. */ void CollectProxyData () { int in, out; /*********/ /* begin */ /*********/ if (dbug) fprintf (stdout, "CollectProxyData()\n"); if (!SystemData.ProxyEnabled) return; SystemData.ProxyLookupCache = accptr->LookupCacheNameCount; SystemData.ProxyLookupDNS = accptr->LookupDnsNameCount; SystemData.ProxyLookupLiteral = accptr->LookupLiteralCount; SystemData.ProxyLookupError = accptr->LookupDnsNameErrorCount; SystemData.ProxyBlocksRawRx64 = pacptr->BlocksRawRx64; SystemData.ProxyBlocksRawTx64 = pacptr->BlocksRawTx64; SystemData.ProxyBytesRawRx64 = pacptr->BytesRawRx64; SystemData.ProxyBytesRawTx64 = pacptr->BytesRawTx64; SystemData.ProxyBytesRawRxTx64 = SystemData.ProxyBytesRawRx64 + SystemData.ProxyBytesRawTx64; SystemData.ProxyReworkCount = pacptr->ReworkCount; SystemData.ProxyReworkNoType = pacptr->ReworkNoType; SystemData.ProxyReworkReplaceCount = pacptr->ReworkReplaceCount; SystemData.ProxyReworkReplaceSearch = pacptr->ReworkReplaceSearch; SystemData.ProxyReworkTooBig = pacptr->ReworkTooBig; SystemData.ProxySocks5Count = pacptr->Socks5Count; SystemData.ProxySocks5Success = pacptr->Socks5SuccessCount; SystemData.ProxySocks5Fail = pacptr->Socks5FailCount; SystemData.ProxyTunnel = 0; for (out = 0; out < TUNNEL_COUNT_MAX; out++) { if (out+1 == PROXY_TUNNEL_FIREWALL) continue; for (in = 0; in < TUNNEL_COUNT_MAX; in++) { if (in+1 == PROXY_TUNNEL_HTTP || in+1 == PROXY_TUNNEL_HTTPS) continue; SystemData.ProxyTunnel += pacptr->TunnelCount[in][out]; } } SystemData.ProxyTunnelCurrent = pacptr->TunnelCurrent; } /*****************************************************************************/ /* Collect scripting data. */ void CollectScriptData () { static ulong PrevCgi, PrevCgiPlus, PrevCgiPlusReused, PrevDclCrePrc, PrevDclDelPrc, PrevDclForceX, PrevRte, PrevRteReused, PrevDclCommand, PrevDECnet, PrevDECnetReused, PrevDECnetCgi, PrevDECnetCgiReused, PrevDECnetOsu, PrevDECnetOsuReused; int idx; /*********/ /* begin */ /*********/ if (dbug) fprintf (stdout, "CollectScriptData()\n"); /* DCL process */ SystemData.CurrentScript = SystemData.CurrentScriptCGIplus = SystemData.CurrentScriptRTE = 0; for (idx = 0; idx <= gblptr->InstanceNodeCurrent; idx++) { SystemData.CurrentScript += accptr->CurrentDclScriptProcess[idx]; SystemData.CurrentScriptCGIplus += accptr->CurrentDclScriptCgiPlus[idx]; SystemData.CurrentScriptRTE += accptr->CurrentDclScriptRTE[idx]; } SystemData.TotalDclCrePrc = accptr->DclCrePrcCount; SystemData.TotalDclCrePrcDetach = accptr->DclCrePrcDetachCount; SystemData.TotalDclCrePrcPersona = accptr->DclCrePrcPersonaCount; SystemData.TotalDclCrePrcPersonaDefault = accptr->DclCrePrcPersonaDefaultCount; SystemData.TotalDclCrePrcPersonaInvUser = accptr->DclCrePrcPersonaInvUserCount; SystemData.TotalDclCrePrcPersonaPrvUser = accptr->DclCrePrcPersonaPrvUserCount; SystemData.TotalDclDelPrc = accptr->DclDelPrcCount; SystemData.TotalDclForceX = accptr->DclForceXCount; SystemData.TotalDclProctor = accptr->DclProctorCount; SystemData.TotalDclHardLimit == accptr->DclHitHardLimitCount; /* totals */ SystemData.TotalCgi = accptr->DoScriptCount; SystemData.TotalCgiPlus = accptr->DoCgiPlusScriptCount; SystemData.TotalCgiPlusReused = accptr->DclCgiPlusReusedCount; SystemData.TotalRte = accptr->DoRteScriptCount; SystemData.TotalRteReused = accptr->DclRteReusedCount; SystemData.TotalAutoScript = accptr->DoAutoScriptCount; SystemData.TotalDclCommand = accptr->DoDclCommandCount; SystemData.TotalDECnet = accptr->DoDECnetCount; SystemData.TotalDECnetReused = accptr->DoDECnetReuseCount; SystemData.TotalDECnetCgi = accptr->DoDECnetCgiCount; SystemData.TotalDECnetCgiReused = accptr->DoDECnetCgiReuseCount; SystemData.TotalDECnetOsu = accptr->DoDECnetOsuCount; SystemData.TotalDECnetOsuReused = accptr->DoDECnetOsuReuseCount; /* deltas */ if (PrevDclCrePrc && SystemData.TotalDclCrePrc > PrevDclCrePrc) SystemData.DeltaDclCrePrc = SystemData.TotalDclCrePrc - PrevDclCrePrc; else SystemData.DeltaDclCrePrc = 0; PrevDclCrePrc = SystemData.TotalDclCrePrc; if (PrevDclDelPrc && SystemData.TotalDclDelPrc > PrevDclDelPrc) SystemData.DeltaDclDelPrc = SystemData.TotalDclDelPrc - PrevDclDelPrc; else SystemData.DeltaDclDelPrc = 0; PrevDclDelPrc = SystemData.TotalDclDelPrc; /* roll those forced to exit into the delprc delta */ if (PrevDclForceX && SystemData.TotalDclForceX > PrevDclForceX) SystemData.DeltaDclDelPrc += (SystemData.TotalDclForceX - PrevDclForceX); PrevDclForceX = SystemData.TotalDclForceX; if (PrevCgi && SystemData.TotalCgi > PrevCgi) SystemData.DeltaCgi = SystemData.TotalCgi - PrevCgi; else SystemData.DeltaCgi = 0; PrevCgi = SystemData.TotalCgi; if (PrevCgiPlus && SystemData.TotalCgiPlus > PrevCgiPlus) SystemData.DeltaCgiPlus = SystemData.TotalCgiPlus - PrevCgiPlus; else SystemData.DeltaCgiPlus = 0; PrevCgiPlus = SystemData.TotalCgiPlus; if (PrevCgiPlusReused && SystemData.TotalCgiPlusReused > PrevCgiPlusReused) SystemData.DeltaCgiPlusReused = SystemData.TotalCgiPlusReused - PrevCgiPlusReused; else SystemData.DeltaCgiPlusReused = 0; PrevCgiPlusReused = SystemData.TotalCgiPlusReused; if (PrevRte && SystemData.TotalRte > PrevRte) SystemData.DeltaRte = SystemData.TotalRte - PrevRte; else SystemData.DeltaRte = 0; PrevRte = SystemData.TotalRte; if (PrevRteReused && SystemData.TotalRteReused > PrevRteReused) SystemData.DeltaRteReused = SystemData.TotalRteReused - PrevRteReused; else SystemData.DeltaRteReused = 0; PrevRteReused = SystemData.TotalRteReused; if (PrevDECnet && SystemData.TotalDECnet > PrevDECnet) SystemData.DeltaDECnet = SystemData.TotalDECnet - PrevDECnet; else SystemData.DeltaDECnet = 0; PrevDECnet = SystemData.TotalDECnet; if (PrevDECnetReused && SystemData.TotalDECnetReused > PrevDECnetReused) SystemData.DeltaDECnetReused = SystemData.TotalDECnetReused - PrevDECnetReused; else SystemData.DeltaDECnetReused = 0; PrevDECnetReused = SystemData.TotalDECnetReused; if (PrevDECnetCgi && SystemData.TotalDECnetCgi > PrevDECnetCgi) SystemData.DeltaDECnetCgi = SystemData.TotalDECnetCgi - PrevDECnetCgi; else SystemData.DeltaDECnetCgi = 0; PrevDECnetCgi = SystemData.TotalDECnetCgi; if (PrevDECnetCgiReused && SystemData.TotalDECnetCgiReused > PrevDECnetCgiReused) SystemData.DeltaDECnetCgiReused = SystemData.TotalDECnetCgiReused - PrevDECnetCgiReused; else SystemData.DeltaDECnetCgiReused = 0; PrevDECnetCgiReused = SystemData.TotalDECnetCgiReused; if (PrevDECnetOsu && SystemData.TotalDECnetOsu > PrevDECnetOsu) SystemData.DeltaDECnetOsu = SystemData.TotalDECnetOsu - PrevDECnetOsu; else SystemData.DeltaDECnetOsu = 0; PrevDECnetOsu = SystemData.TotalDECnetOsu; if (PrevDECnetOsuReused && SystemData.TotalDECnetOsuReused > PrevDECnetOsuReused) SystemData.DeltaDECnetOsuReused = SystemData.TotalDECnetOsuReused - PrevDECnetOsuReused; else SystemData.DeltaDECnetOsuReused = 0; PrevDECnetOsuReused = SystemData.TotalDECnetOsuReused; } /*****************************************************************************/ /* Collect specific data when [x]WebDAV checkbox. */ void CollectWebDavData () { /*********/ /* begin */ /*********/ if (dbug) fprintf (stdout, "CollectWebDavData()\n"); if (!SystemData.WebDavEnabled) return; SystemData.WebDavMetaReadAttemptCount = accptr->WebDavMetaReadAttemptCount; SystemData.WebDavMetaReadCount = accptr->WebDavMetaReadCount; SystemData.WebDavMetaWriteAttemptCount = accptr->WebDavMetaWriteAttemptCount; SystemData.WebDavMetaWriteCount = accptr->WebDavMetaWriteCount; SystemData.WebDavRequestCount = accptr->WebDavRequestCount; SystemData.WebDavXmlParseCount = accptr->WebDavXmlParseCount; SystemData.WebDavBytesRawRx64 = accptr->WebDavBytesRawRx64[HTTP12]; SystemData.WebDavBytesRawTx64 = accptr->WebDavBytesRawTx64[HTTP12]; } /*****************************************************************************/ /* Per-boot system data. */ int CollectStaticData () { static ulong ActiveCpuCnt, AvailCpuCount, ClusterNodes, JpiPid, MemSize, PageSize; static int64 BootTime64; static ushort HwNameLength, PrcNamLength; static uchar ClusterMember; static char ArchName [15+1], HwName [1+60], JpiPrcNam [15+1], NodeName [15+1], VmsVersion [8+1]; /* initialize structure for SYI items */ static struct { short BufferLength; short ItemCode; void *BufferPtr; void *LengthPtr; } SyiItemList[] = { { sizeof(AvailCpuCount), SYI$_AVAILCPU_CNT, &AvailCpuCount, 0 }, { sizeof(ActiveCpuCnt), SYI$_ACTIVECPU_CNT, &ActiveCpuCnt, 0 }, { sizeof(ArchName), SYI$_ARCH_NAME, &ArchName, 0 }, { sizeof(BootTime64), SYI$_BOOTTIME, &BootTime64, 0 }, { sizeof(ClusterMember), SYI$_CLUSTER_MEMBER, &ClusterMember, 0 }, { sizeof(ClusterNodes), SYI$_CLUSTER_NODES, &ClusterNodes, 0 }, { sizeof(HwName), SYI$_HW_NAME, &HwName, &HwNameLength }, { sizeof(MemSize), SYI$_MEMSIZE, &MemSize, 0 }, { sizeof(NodeName), SYI$_SCSNODE, &NodeName, 0 }, { sizeof(PageSize), SYI$_PAGE_SIZE, &PageSize, 0 }, { sizeof(VmsVersion), SYI$_VERSION, &VmsVersion, 0 }, {0,0,0,0} }, JpiItemList[] = { { sizeof(JpiPid), JPI$_PID, &JpiPid, 0 }, { sizeof(JpiPrcNam), JPI$_PRCNAM, &JpiPrcNam, &PrcNamLength }, {0,0,0,0} }; int status; int vnum[2]; char *cptr, *sptr, *zptr; IOSBLK IOsb; /*********/ /* begin */ /*********/ if (dbug) fprintf (stdout, "CollectStaticData()\n"); status = sys$getsyiw ( EfnWait, /* efn */ 0, /* csiaddr */ 0, /* nodename */ &SyiItemList, /* item list */ &IOsb, /* iosb */ 0, /* astaddr */ 0 ); /* astprm */ if (dbug) fprintf (stdout, "sys$getsyi() %%X%08.08X IOsb: %%X%08.08X\n", status, IOsb.iosb$w_status); if (VMSnok (status)) return (status); zptr = (sptr = SystemData.ArchName) + sizeof(SystemData.ArchName)-1; for (cptr = ArchName; *cptr && *cptr != ' ' && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; HwName[HwNameLength] = '\0'; zptr = (sptr = SystemData.HwName) + sizeof(SystemData.HwName)-1; for (cptr = HwName; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; zptr = (sptr = SystemData.NodeName) + sizeof(SystemData.NodeName)-1; for (cptr = NodeName; *cptr && *cptr != ' ' && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; zptr = (sptr = SystemData.VmsVersion) + sizeof(SystemData.VmsVersion)-1; for (cptr = VmsVersion; *cptr && *cptr != ' ' && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; /* i.e. VMS V8.4 is 8040 */ if (sscanf (SystemData.VmsVersion+1, "%d.%d", &vnum[0], &vnum[1]) == 2) SystemData.VmsVersionInteger = vnum[0] * 1000 + vnum[1] * 10; else SystemData.VmsVersionInteger = 0; /* maximum lock value block size is determined by VMS version */ if (SystemData.VmsVersionInteger >= 8020) SystemData.LkiValBlkSize = 64; else SystemData.LkiValBlkSize = 16; SystemData.MemSize = MemSize; SystemData.PageSize = PageSize; SystemData.BootTime64 = BootTime64; SystemData.AvailCpuCount = AvailCpuCount; SystemData.ActiveCpuCount = ActiveCpuCnt; SystemData.ClusterMember = ClusterMember; SystemData.ClusterNodes = ClusterNodes; if (dbug) fprintf (stdout, "|%s|%s|%s|%s| %d %d\n", SystemData.ArchName, SystemData.HwName, SystemData.NodeName, SystemData.VmsVersion, SystemData.AvailCpuCount, ClusterMember); status = sys$getjpiw ( EfnWait, /* efn */ 0, /* csiaddr */ 0, /* nodename */ &JpiItemList, /* item list */ &IOsb, /* iosb */ 0, /* astaddr */ 0 ); /* astprm */ if (dbug) fprintf (stdout, "sys$getjpi() %%X%08.08X IOsb: %%X%08.08X\n", status, IOsb.iosb$w_status); if (VMSnok (status)) return (status); SystemData.JpiPid = JpiPid; JpiPrcNam[PrcNamLength] = '\0'; strcpy (SystemData.aLaModePrcNam, JpiPrcNam); SystemData.ProxyEnabled = pacptr->ServingEnabled; SystemData.WebDavEnabled = accptr->WebDavEnabled; return (status); } /*****************************************************************************/ /* Collect System Performance Information. */ int CollectSPI () { #define INTSTK_MODE 0 #define MPSYNCH_MODE 1 #define KERNEL_MODE 2 #define EXEC_MODE 3 #define SUPER_MODE 4 #define USER_MODE 5 /* 6 is reserved */ #define IDLE_MODE 7 #define MODE_COUNT IDLE_MODE+1 #define CPU_MAX 32 /* structure for a single CPU's mode ticks */ static struct SingleCPU { uchar CPUid; ulong Count [MODE_COUNT]; }; /* a structure all CPU's mode ticks for a system with up to 32 CPUs */ static struct { ulong NumberOfCPUs; struct SingleCPU CPU [CPU_MAX]; } ModeTicks; static ulong BufferedIO, CputTicksBusy, CpuUser, DirectIO, FcpRead, FcpWrite, PrevBufferedIO, PrevCpuTicksBusy, PrevCpuTicksUser, PrevDirectIO, PrevFcpRead, PrevFcpWrite, ProcessCount; #ifdef __ALPHA static ulong FreeList, ModList; #else /* __ia64 || __x86_64 */ static unsigned __int64 FreeList, ModList; #endif /* initialize structure for RMI items */ static struct { short BufferLength; short ItemCode; void *BufferPtr; void *LengthPtr; } ItemList[] = { { sizeof(BufferedIO), GETSPI$_BUFIO, &BufferedIO, 0 }, { sizeof(ModeTicks), GETSPI$_MODES, &ModeTicks, 0 }, { sizeof(DirectIO), GETSPI$_DIRIO, &DirectIO, 0 }, { sizeof(FcpRead), GETSPI$_FCPREAD, &FcpRead, 0 }, { sizeof(FcpWrite), GETSPI$_FCPWRITE, &FcpWrite, 0 }, { sizeof(FreeList), GETSPI$_FRLIST, &FreeList, 0 }, { sizeof(ModList), GETSPI$_MODLIST, &ModList, 0 }, { sizeof(ProcessCount), GETSPI$_PROCS, &ProcessCount, 0 }, {0,0,0,0} }; int cidx, midx, status, tlong; ulong datsize, CpuTicksBusy; IOSBLK IOsb, SynchIOsb; /*********/ /* begin */ /*********/ if (dbug) fprintf (stdout, "CollectSPI()\n"); status = lib$sub_times (&CurrentTime64, &SystemData.BootTime64, &SystemData.UpTime64); if (VMSnok (status)) EXIT_FI_LI (status); /* collect System Performance Information */ status = exe$getspi ( EfnSpi, /* efn */ 0, /* csiaddr */ 0, /* nodename */ &ItemList, /* item list */ &IOsb, /* iosb */ 0, /* astaddr */ 0 ); /* astprm */ if (dbug) fprintf (stdout, "exe$getspi() %%X%08.08X IOsb: %%X%08.08X\n", status, IOsb.iosb$w_status); if (VMSok (status)) status = IOsb.iosb$w_status; if (VMSnok (status)) return (status); /*******/ /* CPU */ /*******/ for (cidx = 1; cidx < ModeTicks.NumberOfCPUs; cidx++) for (midx = INTSTK_MODE; midx <= IDLE_MODE; midx++) ModeTicks.CPU[0].Count[midx] += ModeTicks.CPU[cidx].Count[midx]; CpuTicksBusy = CpuUser = 0; CpuTicksBusy = ModeTicks.CPU[0].Count[INTSTK_MODE]; /* interrupt item is actually the sum of interrupt and idle! */ CpuTicksBusy -= ModeTicks.CPU[0].Count[IDLE_MODE]; CpuTicksBusy += ModeTicks.CPU[0].Count[KERNEL_MODE] + ModeTicks.CPU[0].Count[MPSYNCH_MODE] + ModeTicks.CPU[0].Count[EXEC_MODE] + ModeTicks.CPU[0].Count[SUPER_MODE] + ModeTicks.CPU[0].Count[USER_MODE]; CpuUser = ModeTicks.CPU[0].Count[USER_MODE]; if (PrevCpuTicksBusy && CpuTicksBusy > PrevCpuTicksBusy) { SystemData.CpuTicksBusy = CpuTicksBusy - PrevCpuTicksBusy; SystemData.CpuBusyPercent = (ulong)((float)SystemData.CpuTicksBusy / DeltaSeconds); if (SystemData.CpuBusyPercent > 100 * SystemData.ActiveCpuCount) SystemData.CpuBusyPercent = 100 * SystemData.ActiveCpuCount; } else SystemData.CpuBusyPercent = 0; PrevCpuTicksBusy = CpuTicksBusy; if (PrevCpuTicksUser) { SystemData.CpuTicksUser = CpuUser - PrevCpuTicksUser; SystemData.CpuUserPercent = (ulong)((float)SystemData.CpuTicksUser / DeltaSeconds); if (SystemData.CpuUserPercent > 100 * SystemData.ActiveCpuCount) SystemData.CpuUserPercent = 100 * SystemData.ActiveCpuCount; } else SystemData.CpuUserPercent = 0; PrevCpuTicksUser = CpuUser; if (dbug) fprintf (stdout, "CPU: %d%% USER: %d%%\n", SystemData.CpuBusyPercent, SystemData.CpuUserPercent); /*********/ /* other */ /*********/ if (PrevBufferedIO && BufferedIO > PrevBufferedIO) SystemData.BufferedIO = BufferedIO - PrevBufferedIO; else SystemData.BufferedIO = 0; PrevBufferedIO = BufferedIO; SystemData.BufferedIO = (ulong)((float)SystemData.BufferedIO / DeltaSeconds); if (PrevDirectIO && DirectIO > PrevDirectIO) SystemData.DirectIO = DirectIO - PrevDirectIO; else SystemData.DirectIO = 0; PrevDirectIO = DirectIO; SystemData.DirectIO = (ulong)((float)SystemData.DirectIO / DeltaSeconds); if (PrevFcpRead && FcpRead > PrevFcpRead) SystemData.FcpRead = FcpRead - PrevFcpRead; else SystemData.FcpRead = 0; PrevFcpRead = FcpRead; SystemData.FcpRead = (ulong)((float)SystemData.FcpRead / DeltaSeconds); if (PrevFcpWrite && FcpWrite > PrevFcpWrite) SystemData.FcpWrite = FcpWrite - PrevFcpWrite; else SystemData.FcpWrite = 0; PrevFcpWrite = FcpWrite; SystemData.FcpWrite = (ulong)((float)SystemData.FcpWrite / DeltaSeconds); SystemData.MemInUse = SystemData.MemSize - FreeList - ModList; if (SystemData.MemInUse > SystemData.MemInUseMax) SystemData.MemInUseMax = SystemData.MemInUse; SystemData.ProcessCount = ProcessCount; return (SS$_NORMAL); } /*****************************************************************************/ /* This function uses undocumented sense-mode functionality to load counters from the network interface (commonly ethernet device). Multiple devices are supported using a multi-valued logical name ALAMODE_NI and the frame-rate and byte-rate and soft/hard error-rate statistics are accumlated to represent total system data. Essential concepts have come from "EMON: moniteur ethernet (V2.1-2) Gerard Guillaume" and found in DECUSLIB freeware, and from some peeks at the T4 source code. The implementation is mine. */ void CollectNetIntData () { static int NetIntCount, IoClrCount; static ushort NetIntChan [NI_DEVICE_MAX]; static ulong DcSCOM = 32, /* DC$_SCOM */ DtNI = 13; /* DT$_NI */ static int64 PrevBlocksRx64, PrevBlocksTx64, PrevBytesRx64, PrevBytesRxTx64, PrevBytesTx64, PrevErrorsHard64, PrevErrorsSoft64; static long DeviceCount = -1, DviUnit, LnmIndex = -1, LnmAttributes; static struct { short BufferLength; short ItemCode; void *BufferPtr; void *LengthPtr; } DevScanItems [] = { { sizeof(DcSCOM), DVS$_DEVCLASS, &DcSCOM, 0 }, { 0,0,0,0 } }, GetDviItems [] = { { sizeof(DviUnit), DVI$_UNIT, &DviUnit, 0 }, { 0,0,0,0 } }; static $DESCRIPTOR (NetIntDsc, ""); int ecnt, status; ushort pword, retlen; ulong LnmCount; int64 value64, DevScanContext64, NetIntBlocksRx64, NetIntBlocksTx64, NetIntBytesRx64, NetIntBytesRxTx64, NetIntBytesTx64, NetIntErrorsHard64, NetIntErrorsSoft64; char *cptr, *vptr; char DevScanBuffer [64], CountBuffer [512]; struct STRUCT_IOSBLK IOsb; $DESCRIPTOR (CountDsc, CountBuffer); $DESCRIPTOR (DevScanDsc, DevScanBuffer); /*********/ /* begin */ /*********/ if (dbug) fprintf (stdout, "CollectNetIntData() %s\n", NetIntDev); if (LnmIndex < 0) { /* first call, check for logical name defined network devices */ for (LnmIndex = 0; LnmIndex < NI_DEVICE_MAX; LnmIndex++) { if (!(cptr = SysTrnLnm2 (LOGNAM_NI, NULL, LnmIndex))) break; NetIntDsc.dsc$a_pointer = cptr; NetIntDsc.dsc$w_length = strlen(cptr); status = sys$assign (&NetIntDsc, &NetIntChan[LnmIndex], 0, 0); if (VMSnok (status)) EXIT_FI_LI (status); if (NetIntDev[0]) strcat (NetIntDev, ","); strcat (NetIntDev, cptr); NetIntCount++; } } if (DeviceCount < 0) { /* first call */ DeviceCount = 0; /* if logical name defined and found devices */ if (NetIntCount) return; /* scan system for network devices */ DevScanContext64 = 0; while (NetIntCount < NI_DEVICE_MAX) { status = sys$device_scan (&DevScanDsc, &retlen, 0, &DevScanItems, &DevScanContext64); if (VMSnok (status)) { if (status == SS$_NOMOREDEV || status == SS$_NOSUCHDEV) break; if (VMSnok (status)) EXIT_FI_LI (status); } status = sys$getdviw (EfnWait, 0, &DevScanDsc, &GetDviItems, &IOsb, 0, 0, 0, 0); if (VMSok (status)) status = IOsb.iosb$w_status; if (VMSnok (status)) EXIT_FI_LI (status); /* if not a template device then continue */ if (DviUnit != 0) continue; DevScanBuffer[retlen] = '\0'; /* RMDRIVER gives %SYSTEM-F-SYSVERDIF */ if (!strcmp (DevScanBuffer, "_RMA0:")) continue; status = sys$assign (&DevScanDsc, &NetIntChan[DeviceCount++], 0, 0); if (VMSnok (status)) EXIT_FI_LI (status); if (NetIntDev[0]) strcat (NetIntDev, ","); strcat (NetIntDev, DevScanBuffer+1); NetIntCount++; } /* first call, just return having assigned channels (perhaps) */ return; } /****************/ /* collect data */ /****************/ /* initialise structure and local storage */ NetIntBlocksRx64 = NetIntBlocksTx64 = NetIntBytesRx64 = NetIntBytesTx64 = NetIntBytesRxTx64 = NetIntErrorsHard64 = NetIntErrorsSoft64 = 0; /* if no devices then just return from here */ if (!NetIntCount) return; /* accumulate counts from each device */ for (ecnt = 0; ecnt < NetIntCount; ecnt++) { memset (CountBuffer, 0, sizeof(CountBuffer)); #ifndef IO$M_RD_64COUNT #define IO$M_RD_64COUNT 0x4000 #endif status = sys$qiow (0, NetIntChan[ecnt], IO$_SENSEMODE | IO$M_RD_COUNT | IoClrCount | IO$M_RD_64COUNT | IO$M_CTRL, &IOsb, 0, 0, 0, &CountDsc, 0, 0, 0, 0); if (VMSok (status)) status = IOsb.iosb$w_status; if (VMSnok (status)) EXIT_FI_LI (status); vptr = CountBuffer; while (vptr < CountBuffer + IOsb.iosb$w_bcnt) { pword = *(USHORTPTR)vptr; vptr += 2; /* end of list */ if (!pword) break; /* MSB should be set! */ if (!(pword & 0x8000)) break; value64 = 0; if ((pword & 0x0fff) < 200) { /* quadword value (item number less than 200) */ value64 = *(UINT64PTR)vptr; vptr += 8; } else if ((pword & 0x6000) == 0x6000) { /* longword value */ value64 = *(ULONGPTR)vptr; vptr += 4; } else if ((pword & 0x6000) == 0x4000) { /* word value */ value64 = *(USHORTPTR)vptr; vptr += 2; } else if ((pword & 0x6000) == 0x2000) { /* byte value */ value64 = *(uchar*)vptr; vptr++; } if (pword & 0x1000) { /* hw map (?) */ value64 = *(ULONGPTR)vptr; vptr += 2; continue; } switch (pword & 0x0fff) { /* these look suspiciously like "LANCP SHOW DEVICE /COUNT"! */ case 1 : /* seconds since last zeroed */ break; case 2 : case 6 : NetIntBytesRx64 += value64; break; case 3 : case 7 : NetIntBytesTx64 += value64; break; case 4 : case 8 : NetIntBlocksRx64 += value64; break; case 5 : case 9 : NetIntBlocksTx64 += value64; break; case 12 : case 13 : case 14 : case 15 : case 16 : case 17 : case 18 : case 19 : case 30 : case 21 : case 22 : case 23 : case 24 : case 27 : case 28 : case 29 : NetIntErrorsSoft64 += value64; break; case 25 : case 26 : case 33 : case 34 : NetIntErrorsHard64 += value64; break; /* more "traditional" counters */ case 1000 /* NMA$C_CTLIN_BRC bytes received */ : case 1002 /* NMA$C_CTLIN_MBY multicast bytes received */ : NetIntBytesRx64 += value64; break; case 1001 /* NMA$C_CTLIN_BSN bytes sent */ : NetIntBytesTx64 += value64; break; case 1010 /* NMA$C_CTLIN_DBR data blocks received */ : case 1012 /* NMA$C_CTLIN_MBL multicast blocks received */ : NetIntBlocksRx64 += value64; break; case 1011 /* NMA$C_CTLIN_DBS data blocks sent */ : NetIntBlocksTx64 += value64; break; case 1013 /* NMA$C_CTLIN_BID */ : case 1014 /* NMA$C_CTLIN_BS1 */ : case 1015 /* NMA$C_CTLIN_BSM */ : case 1040 /* NMA$C_CTLIN_RBE */ : case 1041 /* NMA$C_CTLIN_LBE */ : case 1064 /* NMA$C_CTLIN_OVR */ : case 1066 /* NMA$C_CTLIN_UBU */ : NetIntErrorsSoft64 += value64; break; case 1060 /* NMA$C_CTLIN_SFL */ : case 1061 /* NMA$C_CTLIN_CDC */ : case 1063 /* NMA$C_CTLIN_UFD */ : case 1062 /* NMA$C_CTLIN_RFL */ : case 1065 /* NMA$C_CTLIN_SBU */ : NetIntErrorsHard64 += value64; break; default : } } } if (PrevBytesRx64 && NetIntBytesRx64 > PrevBytesRx64) SystemData.NetIntBytesRx64 = NetIntBytesRx64 - PrevBytesRx64; else SystemData.NetIntBytesRx64 = 0; PrevBytesRx64 = NetIntBytesRx64; if (PrevBytesTx64 && NetIntBytesTx64 > PrevBytesTx64) SystemData.NetIntBytesTx64 = NetIntBytesTx64 - PrevBytesTx64; else SystemData.NetIntBytesTx64 = 0; PrevBytesTx64 = NetIntBytesTx64; NetIntBytesRxTx64 = NetIntBytesRx64 + NetIntBytesTx64; if (PrevBytesRxTx64 && NetIntBytesRxTx64 > PrevBytesRxTx64) SystemData.NetIntBytesRxTx64 = NetIntBytesRxTx64 - PrevBytesRxTx64; else SystemData.NetIntBytesRxTx64 = 0; PrevBytesRxTx64 = NetIntBytesRxTx64; SystemData.NetBytesRxPerSec = (ulong)(float)SystemData.NetIntBytesRx64 / (float)DeltaSeconds; SystemData.NetBytesTxPerSec = (ulong)(float)SystemData.NetIntBytesTx64 / (float)DeltaSeconds; SystemData.NetBytesPerSec = (ulong)(float)SystemData.NetIntBytesRxTx64 / (float)DeltaSeconds; if (PrevBlocksRx64 && NetIntBlocksRx64 > PrevBlocksRx64) SystemData.NetIntBlocksRx64 = NetIntBlocksRx64 - PrevBlocksRx64; else SystemData.NetIntBlocksRx64 = 0; PrevBlocksRx64 = NetIntBlocksRx64; if (PrevBlocksTx64 && NetIntBlocksTx64 > PrevBlocksTx64) SystemData.NetIntBlocksTx64 = NetIntBlocksTx64 - PrevBlocksTx64; else SystemData.NetIntBlocksTx64 = 0; PrevBlocksTx64 = NetIntBlocksTx64; if (SystemData.NetIntBlocksRx64) SystemData.NetDatagramRx = (ulong)(float)SystemData.NetIntBytesRx64 / (float)SystemData.NetIntBlocksRx64 / (float)DeltaSeconds; else SystemData.NetDatagramRx = 0; if (SystemData.NetIntBlocksTx64) SystemData.NetDatagramTx = (ulong)(float)SystemData.NetIntBytesTx64 / (float)SystemData.NetIntBlocksTx64 / (float)DeltaSeconds; else SystemData.NetDatagramTx = 0; if (PrevErrorsSoft64 && NetIntErrorsSoft64 > PrevErrorsSoft64) SystemData.NetIntErrorsSoft64 = NetIntErrorsSoft64 - PrevErrorsSoft64; PrevErrorsSoft64 = NetIntErrorsSoft64; if (PrevErrorsHard64 && NetIntErrorsHard64 > PrevErrorsHard64) SystemData.NetIntErrorsHard64 = NetIntErrorsHard64 - PrevErrorsHard64; PrevErrorsHard64 = NetIntErrorsHard64; } /*****************************************************************************/ /* See [SRC.HTTPD]CONTROL.C for other information. Uses the VMS Distributed Lock Manager to keep track of how many servers are currently executing on the node/cluster. NL locks indicate interest (used by this utility), CR locks indicate a server waiting for a group directive. This function enqueues a single NL lock, then periodically get all the locks associated with that resource and counts up the number of CR locks - giving the number of servers! */ void CollectInstanceData () { static int InstanceCount, PrevClusterCount = -1, PrevNodeCount = -1; static ulong InstancePid [INSTANCE_MAX]; static char ClusterLockName [32], NodeLockName [32]; static $DESCRIPTOR (ClusterLockNameDsc, ClusterLockName); static $DESCRIPTOR (NodeLockNameDsc, NodeLockName); static LKIDEF LkiLocks [32]; static struct lksb ClusterLockLksb, NodeLockLksb; static struct { ushort TotalLength, /* bits 0..15 */ LockLength; /* bits 16..30 */ } ReturnLength; static struct { ushort buf_len; ushort item; uchar *buf_addr; void *ret_len; } LkiItems [] = { { sizeof(LkiLocks), LKI$_LOCKS, &LkiLocks, &ReturnLength }, {0,0,0,0} }; int cnt, status, ClusterCount, LockCount, LockNameMagic, NodeCount, NonNlLockCount; char *cptr, *sptr, *zptr; IOSBLK IOsb; LKIDEF *lkiptr; /*********/ /* begin */ /*********/ if (dbug) fprintf (stdout, "CollectInstanceData()\n"); if (!ClusterLockName[0]) { /**************/ /* initialise */ /**************/ if (InstanceEnvNumber > INSTANCE_ENV_NUMBER_MAX) EXIT_FI_LI (STS$K_ERROR | STS$M_INHIB_MSG); /* a byte comprising two 4 bit fields, version and server group number */ LockNameMagic = ((HTTPD_LOCK_VERSION & 0xf) << 4) | (InstanceEnvNumber & 0xf); /* build the (binary) resource name for the cluster lock */ zptr = (sptr = ClusterLockName) + sizeof(ClusterLockName)-1; for (cptr = HTTPD_NAME; *cptr && sptr < zptr; *sptr++ = *cptr++); if (sptr < zptr) *sptr++ = (char)LockNameMagic; if (sptr < zptr) *sptr++ = (char)INSTANCE_CLUSTER; ClusterLockNameDsc.dsc$w_length = sptr - ClusterLockName; /* enqueue a just-interested NL lock */ status = sys$enqw (0, LCK$K_NLMODE, &ClusterLockLksb, LCK$M_EXPEDITE | LCK$M_SYSTEM, &ClusterLockNameDsc, 0, 0, 0, 0, 0, 2, 0); if (VMSok (status)) status = ClusterLockLksb.lksb$w_status; if (VMSnok (status)) EXIT_FI_LI (status); /* build the (binary) resource name for the node lock */ zptr = (sptr = NodeLockName) + sizeof(NodeLockName)-1; for (cptr = HTTPD_NAME; *cptr && sptr < zptr; *sptr++ = *cptr++); if (sptr < zptr) *sptr++ = (char)LockNameMagic; for (cptr = SystemData.NodeName; *cptr && sptr < zptr; *sptr++ = *cptr++); if (sptr < zptr) *sptr++ = (char)INSTANCE_NODE; NodeLockNameDsc.dsc$w_length = sptr - NodeLockName; /* enqueue a just-interested NL lock */ status = sys$enqw (0, LCK$K_NLMODE, &NodeLockLksb, LCK$M_EXPEDITE | LCK$M_SYSTEM, &NodeLockNameDsc, 0, 0, 0, 0, 0, 2, 0); if (VMSok (status)) status = NodeLockLksb.lksb$w_status; if (VMSnok (status)) EXIT_FI_LI (status); } /*******************/ /* count instances */ /*******************/ /* get and count the cluster instance locks */ status = sys$getlkiw (EfnWait, &ClusterLockLksb.lksb$l_lkid, &LkiItems, &IOsb, 0, 0, 0); if (VMSok (status)) status = ClusterLockLksb.lksb$w_status; if (VMSnok (status)) EXIT_FI_LI (status); if (ReturnLength.LockLength) { /* if insufficient buffer space */ if (ReturnLength.LockLength & 0x8000) EXIT_FI_LI (SS$_BADPARAM); LockCount = ReturnLength.TotalLength / ReturnLength.LockLength; } else LockCount = 0; if (dbug) fprintf (stdout, "%d\n", LockCount); ClusterCount = 0; lkiptr = &LkiLocks; for (cnt = LockCount; cnt; cnt--) { if (dbug) fprintf (stdout, "lki$b_grmode: %d\n", lkiptr->lki$b_grmode); if (lkiptr->lki$b_grmode != LCK$K_NLMODE) ClusterCount++; lkiptr++; } /* get and count the node instance locks */ status = sys$getlkiw (EfnWait, &NodeLockLksb.lksb$l_lkid, &LkiItems, &IOsb, 0, 0, 0); if (VMSok (status)) status = NodeLockLksb.lksb$w_status; if (VMSnok (status)) EXIT_FI_LI (status); if (ReturnLength.LockLength) { /* if insufficient buffer space */ if (ReturnLength.LockLength & 0x8000) EXIT_FI_LI (SS$_BADPARAM); LockCount = ReturnLength.TotalLength / ReturnLength.LockLength; } else LockCount = 0; if (dbug) fprintf (stdout, "%d\n", LockCount); NodeCount = 0; for (cnt = 0; cnt < INSTANCE_MAX; cnt++) InstancePid[cnt] = 0; lkiptr = &LkiLocks; for (cnt = LockCount; cnt; cnt--) { if (dbug) fprintf (stdout, "lki$b_grmode: %d %08.08X\n", lkiptr->lki$b_grmode, lkiptr->lki$l_pid); if (lkiptr->lki$b_grmode != LCK$K_NLMODE) InstancePid[NodeCount++] = lkiptr->lki$l_pid; lkiptr++; } if (++InstanceCount > NodeCount) InstanceCount = 1; if (PrevClusterCount < 0) { /* initialize */ PrevClusterCount = ClusterCount; PrevNodeCount = NodeCount; } SystemData.InstanceNodeCount = NodeCount; SystemData.InstanceClusterCount = ClusterCount; for (cnt = 0; cnt < INSTANCE_MAX; cnt++) SystemData.InstanceNodePid[cnt] = InstancePid[cnt]; } /*****************************************************************************/ /* See [SRC.HTTPD]CONTROL.C for other information. Uses the VMS Distributed Lock Manager. Called directly with a NULL parameter to initialise locks associated with distributed node and cluster commands (/DO= and the Server Admin equivalents). Once initialised is called with the relevant lock status block pointed to report an issued control command. Retrieve The EX (exclusive) lock enqueued by the controlling process has been dequeued allowing this AST to be delivered. The control command is then in the lock value block. */ void ReportInstanceControl (struct lksb *lksbptr) { #undef LKI$_XVALNOTVALID #define LKI$_XVALNOTVALID 531 #undef LCK$M_XVALBLK #define LCK$M_XVALBLK 0x10000 static char ClusterDoLockName [32], NodeDoLockName [32]; static $DESCRIPTOR (ClusterDoLockNameDsc, ClusterDoLockName); static $DESCRIPTOR (NodeDoLockNameDsc, NodeDoLockName); static struct lksb ClusterDoLockLksb, NodeDoLockLksb; int status, EnqueueFlags, LockNameMagic; char *cptr, *sptr, *zptr; /*********/ /* begin */ /*********/ if (lksbptr == NULL) { /**************/ /* initialise */ /**************/ /* a byte comprising two 4 bit fields, version and server group number */ LockNameMagic = ((HTTPD_LOCK_VERSION & 0xf) << 4) | (InstanceEnvNumber & 0xf); /****************/ /* cluster /do= */ /****************/ /* build the (binary) resource name for the cluster DO lock */ zptr = (sptr = ClusterDoLockName) + sizeof(ClusterDoLockName)-1; for (cptr = HTTPD_NAME; *cptr && sptr < zptr; *sptr++ = *cptr++); if (sptr < zptr) *sptr++ = (char)LockNameMagic; if (sptr < zptr) *sptr++ = (char)INSTANCE_CLUSTER_DO; ClusterDoLockNameDsc.dsc$w_length = sptr - ClusterDoLockName; /* enqueue a just-interested NL lock */ EnqueueFlags = LCK$M_EXPEDITE | LCK$M_SYSTEM; status = sys$enqw (0, LCK$K_NLMODE, &ClusterDoLockLksb, EnqueueFlags, &ClusterDoLockNameDsc, 0, 0, 0, 0, 0, 2, 0); if (VMSok (status)) status = ClusterDoLockLksb.lksb$w_status; if (VMSnok (status)) EXIT_FI_LI (status); /* convert (wait) to CR to block the queue against EX mode */ EnqueueFlags = LCK$M_VALBLK | LCK$M_QUECVT | LCK$M_CONVERT | LCK$M_SYSTEM; if (SystemData.LkiValBlkSize == 64) EnqueueFlags |= LCK$M_XVALBLK; status = sys$enqw (0, LCK$K_CRMODE, &ClusterDoLockLksb, EnqueueFlags, 0, 0, 0, &ClusterDoLockLksb, &ReportInstanceControl, 0, 2, 0); if (VMSok (status)) status = ClusterDoLockLksb.lksb$w_status; if (status == SS$_VALNOTVALID) status = SS$_NORMAL; if (status == SS$_XVALNOTVALID) { /* hmmm, change in cluster composition? whatever! back to 16 bytes */ SystemData.LkiValBlkSize = 16; status = SS$_NORMAL; } if (VMSnok (status)) EXIT_FI_LI (status); /*************/ /* node /do= */ /*************/ /* build the (binary) resource name for the node DO lock */ zptr = (sptr = NodeDoLockName) + sizeof(NodeDoLockName)-1; for (cptr = HTTPD_NAME; *cptr && sptr < zptr; *sptr++ = *cptr++); if (sptr < zptr) *sptr++ = (char)LockNameMagic; for (cptr = SystemData.NodeName; *cptr && sptr < zptr; *sptr++ = *cptr++); if (sptr < zptr) *sptr++ = (char)INSTANCE_NODE_DO; NodeDoLockNameDsc.dsc$w_length = sptr - NodeDoLockName; /* enqueue a just-interested NL lock */ EnqueueFlags = LCK$M_EXPEDITE | LCK$M_SYSTEM; status = sys$enqw (0, LCK$K_NLMODE, &NodeDoLockLksb, EnqueueFlags, &NodeDoLockNameDsc, 0, 0, 0, 0, 0, 2, 0); if (VMSok (status)) status = NodeDoLockLksb.lksb$w_status; if (VMSnok (status)) EXIT_FI_LI (status); /* convert (wait) to CR to block the queue against EX mode */ EnqueueFlags = LCK$M_VALBLK | LCK$M_QUECVT | LCK$M_CONVERT | LCK$M_SYSTEM; if (SystemData.LkiValBlkSize == 64) EnqueueFlags |= LCK$M_XVALBLK; status = sys$enqw (0, LCK$K_CRMODE, &NodeDoLockLksb, EnqueueFlags, 0, 0, 0, &NodeDoLockLksb, &ReportInstanceControl, 0, 2, 0); if (VMSok (status)) status = NodeDoLockLksb.lksb$w_status; if (status == SS$_VALNOTVALID) status = SS$_NORMAL; if (status == SS$_XVALNOTVALID) { /* hmmm, change in cluster composition? whatever! back to 16 bytes */ SystemData.LkiValBlkSize = 16; status = SS$_NORMAL; } if (VMSnok (status)) EXIT_FI_LI (status); } else { /********/ /* /DO= */ /********/ /* convert (wait) current CR mode lock to NL unblocking the queue */ EnqueueFlags = LCK$M_CONVERT | LCK$M_SYSTEM; status = sys$enqw (0, LCK$K_NLMODE, lksbptr, EnqueueFlags, 0, 0, 0, 0, 0, 0, 2, 0); if (VMSok (status)) status = lksbptr->lksb$w_status; if (VMSnok (status)) EXIT_FI_LI (status); /* convert (wait) back to CR to block queue against next EX mode */ EnqueueFlags = LCK$M_VALBLK | LCK$M_QUECVT | LCK$M_CONVERT | LCK$M_SYSTEM; if (SystemData.LkiValBlkSize == 64) EnqueueFlags |= LCK$M_XVALBLK; status = sys$enqw (0, LCK$K_CRMODE, lksbptr, EnqueueFlags, 0, 0, 0, lksbptr, &ReportInstanceControl, 0, 2, 0); if (VMSok (status)) status = lksbptr->lksb$w_status; if (status == SS$_VALNOTVALID) status = SS$_NORMAL; if (status == SS$_XVALNOTVALID) { /* hmmm, change in cluster composition? whatever! back to 16 bytes */ SystemData.LkiValBlkSize = 16; status = SS$_NORMAL; } if (VMSnok (status)) EXIT_FI_LI (status); /* the lock value block written by the dequeued EX mode lock */ cptr = (char*)lksbptr->lksb$b_valblk; if (*cptr) JsonAlert (NULL, "#/DO=%s%s", cptr, lksbptr == &ClusterDoLockLksb ? "/ALL" : ""); } } /*****************************************************************************/ /* Collect (server) per-process data. */ int CollectProcessData ( ulong ProcessId, PROCESS_DATA *DataPtr ) { /* e.g. U-S-E-K- U+S-E-K- U^S-E-K- */ #define ASTFLG(bit,idx) { if (JpiAstEn & bit) if (JpiAstAct & bit) \ pdptr->AstFlags[idx] = '^'; \ else pdptr->AstFlags[idx] = '+'; \ else pdptr->AstFlags[idx] = '-'; } static long JpiAstAct, JpiAstCnt, JpiAstEn, JpiAstLm, JpiBioCnt, JpiBioLm, JpiBufIo, JpiBytCnt, JpiCpuId, JpiCpuTim, JpiDioCnt, JpiDioLm, JpiDirIo, JpiEnqCnt, JpiEnqLm, JpiExecTim, JpiFilCnt, JpiFilLm, JpiControlFlags = JPI$M_IGNORE_TARGET_STATUS, JpiGpgCnt, JpiJobPrcCnt, JpiJobType, JpiKtCount, JpiKrnlTim, JpiMode, JpiMultiThread, JpiOwner, JpiPageFlts, JpiPagFilCnt, JpiPgFlQuota, JpiPid, JpiPpgCnt, JpiPrcCnt, JpiPrcLm, JpiPri, JpiPrib, JpiState, JpiSts, JpiSts2, JpiSuprTim, JpiTqCnt, JpiTqLm, JpiUserTim, JpiVolumes, JpiWsAuth, JpiWsAuthExt, JpiWsExtent, JpiWsPeak, JpiWsQuota, JpiWsSize, UaiBytLm; static int64 JpiFrePteCnt64, JpiLoginTim64, JpiVirtPeak64; static char JpiPrcNam [JPI_PRCNAM_MAX+1], JpiUserName [12+1]; static $DESCRIPTOR (UserNameDsc, JpiUserName); /* initialize structure for SPI items */ static struct { short BufferLength; short ItemCode; void *BufferPtr; void *LengthPtr; } ItemListJpi[] = { { sizeof(JpiControlFlags), JPI$_GETJPI_CONTROL_FLAGS, &JpiControlFlags, 0 }, { sizeof(JpiAstAct), JPI$_ASTACT, &JpiAstAct, 0 }, { sizeof(JpiAstCnt), JPI$_ASTCNT, &JpiAstCnt, 0 }, { sizeof(JpiAstEn), JPI$_ASTEN, &JpiAstEn, 0 }, { sizeof(JpiAstLm), JPI$_ASTLM, &JpiAstLm, 0 }, { sizeof(JpiBioCnt), JPI$_BIOCNT, &JpiBioCnt, 0 }, { sizeof(JpiBioLm), JPI$_BIOLM, &JpiBioLm, 0 }, { sizeof(JpiBufIo), JPI$_BUFIO, &JpiBufIo, 0 }, { sizeof(JpiBytCnt), JPI$_BYTCNT, &JpiBytCnt, 0 }, { sizeof(JpiCpuId), JPI$_CPU_ID, &JpiCpuId, 0 }, { sizeof(JpiCpuTim), JPI$_CPUTIM, &JpiCpuTim, 0 }, { sizeof(JpiDioCnt), JPI$_DIOCNT, &JpiDioCnt, 0 }, { sizeof(JpiDioLm), JPI$_DIOLM, &JpiDioLm, 0 }, { sizeof(JpiDirIo), JPI$_DIRIO, &JpiDirIo, 0 }, { sizeof(JpiEnqCnt), JPI$_ENQCNT, &JpiEnqCnt, 0 }, { sizeof(JpiEnqLm), JPI$_ENQLM, &JpiEnqLm, 0 }, { sizeof(JpiFilCnt), JPI$_FILCNT, &JpiFilCnt, 0 }, { sizeof(JpiFilLm), JPI$_FILLM, &JpiFilLm, 0 }, { sizeof(JpiFrePteCnt64), JPI$_FREPTECNT, &JpiFrePteCnt64, 0 }, { sizeof(JpiGpgCnt), JPI$_GPGCNT, &JpiGpgCnt, 0 }, { sizeof(JpiJobPrcCnt), JPI$_JOBPRCCNT, &JpiJobPrcCnt, 0 }, { sizeof(JpiJobType), JPI$_JOBTYPE, &JpiJobType, 0 }, { sizeof(JpiKtCount), JPI$_KT_COUNT, &JpiKtCount, 0 }, { sizeof(JpiMultiThread), JPI$_MULTITHREAD, &JpiMultiThread, 0 }, { sizeof(JpiOwner), JPI$_OWNER, &JpiOwner, 0 }, { sizeof(JpiPageFlts), JPI$_PAGEFLTS, &JpiPageFlts, 0 }, { sizeof(JpiPagFilCnt), JPI$_PAGFILCNT, &JpiPagFilCnt, 0 }, { sizeof(JpiPgFlQuota), JPI$_PGFLQUOTA, &JpiPgFlQuota, 0 }, { sizeof(JpiPid), JPI$_PID, &JpiPid, 0 }, { sizeof(JpiPpgCnt), JPI$_PPGCNT, &JpiPpgCnt, 0 }, { sizeof(JpiPrcCnt), JPI$_PRCCNT, &JpiPrcCnt, 0 }, { sizeof(JpiPrcLm), JPI$_PRCLM, &JpiPrcLm, 0 }, { sizeof(JpiPri), JPI$_PRI, &JpiPri, 0 }, { sizeof(JpiPrib), JPI$_PRIB, &JpiPrib, 0 }, { sizeof(JpiState), JPI$_STATE, &JpiState, 0 }, { sizeof(JpiTqCnt), JPI$_TQCNT, &JpiTqCnt, 0 }, { sizeof(JpiTqLm), JPI$_TQLM, &JpiTqLm, 0 }, { sizeof(JpiVirtPeak64), JPI$_VIRTPEAK, &JpiVirtPeak64, 0 }, { sizeof(JpiVolumes), JPI$_VOLUMES, &JpiVolumes, 0 }, { sizeof(JpiWsAuth), JPI$_WSAUTH, &JpiWsAuth, 0 }, { sizeof(JpiWsAuthExt), JPI$_WSAUTHEXT, &JpiWsAuthExt, 0 }, { sizeof(JpiWsExtent), JPI$_WSEXTENT, &JpiWsExtent, 0 }, { sizeof(JpiWsPeak), JPI$_WSPEAK, &JpiWsPeak, 0 }, { sizeof(JpiWsQuota), JPI$_WSQUOTA, &JpiWsQuota, 0 }, { sizeof(JpiWsSize), JPI$_WSSIZE, &JpiWsSize, 0 }, { sizeof(JpiLoginTim64), JPI$_LOGINTIM, &JpiLoginTim64, 0 }, { sizeof(JpiPrcNam), JPI$_PRCNAM, &JpiPrcNam, 0 }, { sizeof(JpiExecTim), JPI$_EXECTIM, &JpiExecTim, 0 }, { sizeof(JpiKrnlTim), JPI$_KRNLTIM, &JpiKrnlTim, 0 }, { sizeof(JpiSuprTim), JPI$_SUPRTIM, &JpiSuprTim, 0 }, { sizeof(JpiUserTim), JPI$_USERTIM, &JpiUserTim, 0 }, { sizeof(JpiUserName), JPI$_USERNAME, &JpiUserName, 0 }, {0,0,0,0} }, ItemListUai [] = { { sizeof(UaiBytLm), UAI$_BYTLM, &UaiBytLm, NULL }, {0,0,0,0} }; int status; char *cptr, *sptr, *zptr; ulong pid, BufIo, CpuTime, CpuSekTime, CpuPercent, CpuSekPercent, DirIo, PageFlts; int64 CpuTime64, CpuExecTime64, CpuKrnlTime64, CpuSuprTime64, CpuUserTime64; unsigned short slen; IOSBLK IOsb; PROCESS_DATA *pdptr; /*********/ /* begin */ /*********/ if (dbug) fprintf (stdout, "CollectProcData() %08.08X\n", ProcessId); memset (pdptr = DataPtr, 0, sizeof(PROCESS_DATA)); status = sys$getjpiw ( EfnWait, /* efn */ &ProcessId, /* pidaddr */ 0, /* prcnam */ &ItemListJpi, /* item list */ &IOsb, /* iosb */ 0, /* astaddr */ 0 ); /* astprm */ if (dbug) fprintf (stdout, "sys$getjpi() %%X%08.08X IOsb: %%X%08.08X\n", status, IOsb.iosb$w_status); if (VMSok (status)) status = IOsb.iosb$w_status; if (VMSnok (status)) return (status); if (!UaiBytLm) { /* This is a workaround for establishing initial BYTLM. Apparently there is a long-standing issue getting representative BYTLM values from $GETJPI and F$GETJPI (and confirmed by alamode). Search the 'net for "F$GETJPI using BYTLM item returns BYTCNT". */ for (cptr = JpiUserName; (cptr-JpiUserName<=12) && *cptr != ' '; cptr++); UserNameDsc.dsc$w_length = cptr - JpiUserName; status = sys$getuai ( 0, /* reserved */ 0, /* context */ &UserNameDsc, /* username */ &ItemListUai, /* item list */ &IOsb, /* iosb */ 0, /* astaddr */ 0 ); /* astprm */ if (dbug) fprintf (stdout, "sys$getuai() %%X%08.08X IOsb: %%X%08.08X\n", status, IOsb.iosb$w_status); if (VMSok (status)) status = IOsb.iosb$w_status; if (VMSnok (status)) return (status); /* bit q&d but ... no longer need the JPI$_USERNAME item */ memset (&ItemListJpi[(sizeof(ItemListJpi)/12)-2],0,12); } /* terminate and trim trailing spaces */ JpiPrcNam[sizeof(JpiPrcNam)-1] = '\0'; for (sptr = JpiPrcNam; *sptr; sptr++); *sptr-- = '\0'; while (sptr > JpiPrcNam && *sptr == ' ') sptr--; *++sptr = '\0'; strcpy (pdptr->JpiPrcNam, JpiPrcNam); lib$sub_times (&CurrentTime64, &JpiLoginTim64, &pdptr->ConTime64); /* make the 10mS tick a delta time */ CpuTime64 = JpiCpuTim * -100000; pdptr->CpuTime64 = CpuTime64; /* make the 10mS ticks into delta times */ CpuKrnlTime64 = JpiKrnlTim * -100000; CpuExecTime64 = JpiExecTim * -100000; CpuSuprTime64 = JpiSuprTim * -100000; CpuUserTime64 = JpiUserTim * -100000; pdptr->JpiAstCnt = JpiAstCnt; pdptr->JpiAstLm = JpiAstLm; pdptr->JpiBioCnt = JpiBioCnt; pdptr->JpiBioLm = JpiBioLm; pdptr->JpiBytCnt = JpiBytCnt; pdptr->JpiBytLm = UaiBytLm; /* NOTE this workaround */ pdptr->JpiBufIo = JpiBufIo; pdptr->JpiCpuId = JpiCpuId; pdptr->JpiDioCnt = JpiDioCnt; pdptr->JpiDirIo = JpiDirIo; pdptr->JpiDioLm = JpiDioLm; pdptr->JpiEnqCnt = JpiEnqCnt; pdptr->JpiEnqLm = JpiEnqLm; pdptr->JpiFilCnt = JpiFilCnt; pdptr->JpiFilLm = JpiFilLm; pdptr->JpiGpgCnt = JpiGpgCnt; pdptr->JpiKtCount = JpiKtCount; pdptr->JpiMultiThread = JpiMultiThread; pdptr->JpiPagFilCnt = JpiPagFilCnt; pdptr->JpiPageFlts = JpiPageFlts; pdptr->JpiPgFlQuota = JpiPgFlQuota; pdptr->JpiPpgCnt = JpiPpgCnt; pdptr->JpiPrcCnt = JpiPrcCnt; pdptr->JpiPrcLm = JpiPrcLm; pdptr->JpiPri = JpiPri; pdptr->JpiPrib = JpiPrib; pdptr->JpiTqLm = JpiTqLm; pdptr->JpiTqCnt = JpiTqCnt; pdptr->JpiWsExtent = JpiWsExtent; pdptr->JpiWsPeak = JpiWsPeak; pdptr->JpiWsQuota = JpiWsQuota; pdptr->JpiVirtPeak64 = JpiVirtPeak64; pdptr->JpiVolumes = JpiVolumes; /* derive the number of page faults during the period */ if (pdptr->ProcessId != ProcessId) { pdptr->ProcessId = ProcessId; pdptr->PrevPageFaults = 0; } if (pdptr->PrevPageFaults) pdptr->PageFaults = JpiPageFlts - pdptr->PrevPageFaults; pdptr->PrevPageFaults = JpiPageFlts; switch (JpiState) { case 1 : pdptr->ProcessState = "COLPG"; break; case 2 : pdptr->ProcessState = "MWAIT"; break; case 3 : pdptr->ProcessState = "CEF"; break; case 4 : pdptr->ProcessState = "PFW"; break; case 5 : pdptr->ProcessState = "LEF"; break; case 6 : pdptr->ProcessState = "LEFO"; break; case 7 : pdptr->ProcessState = "HIB"; break; case 8 : pdptr->ProcessState = "HIBO"; break; case 9 : pdptr->ProcessState = "SUSP"; break; case 10 : pdptr->ProcessState = "SUSPO"; break; case 11 : pdptr->ProcessState = "FPG"; break; case 12 : pdptr->ProcessState = "COM"; break; case 13 : pdptr->ProcessState = "COMO"; break; case 14 : pdptr->ProcessState = "CUR"; break; default : pdptr->ProcessState = "?"; } /* AST flags */ pdptr->AstFlags[0] = 'U'; ASTFLG(1,1) pdptr->AstFlags[2] = 'S'; ASTFLG(2,3) pdptr->AstFlags[4] = 'E'; ASTFLG(4,5) pdptr->AstFlags[6] = 'K'; ASTFLG(8,7) pdptr->AstFlags[8] = '\0'; return (SS$_NORMAL); } /*****************************************************************************/ /* If |Name| is non-NULL lookup the IP address using the host name. If |Address| is non-NULL lookup the host name using the address. If the Ip4AddtrPtr and/or Ip6AddrPtr are non-NULL populate them. Return either a pointer to the resolved host name or IP address string or an error message between square brackets. */ char* TcpIpLookup ( char *Name, char *Address, uchar *Ip4Addr, uchar *Ip6Addr ) { #define CACHE_MAX 16 static aCacheIdx, nCacheIdx; static char abuf [CACHE_MAX][256], ares [CACHE_MAX][256], nbuf [CACHE_MAX][256], nres [CACHE_MAX][256]; int idx, retry, retval; void *addptr; struct sockaddr_in addr4; struct sockaddr_in6 addr6; struct addrinfo hints; struct addrinfo *aiptr; /*********/ /* begin */ /*********/ if (dbug) fprintf (stdout, "TcpIpLookup() |%s|%s| %d %d\n", Name, Address, Ip4Addr, Ip6Addr); if (Ip4Addr) memset (Ip4Addr, 0, 4); if (Ip6Addr) memset (Ip6Addr, 0, 16); if (Name) { for (idx = 0; idx < CACHE_MAX; idx++) if (!strcmp (nbuf[idx], Name)) return (nres[idx]); idx = nCacheIdx++ % CACHE_MAX; strcpy (nbuf[idx], Name); aiptr = NULL; memset (&hints, 0, sizeof(hints)); hints.ai_flags |= AI_CANONNAME; retval = 0; for (retry = TCPIP_LOOKUP_HOST_NAME_RETRY; retry; retry--) { retval = getaddrinfo (Name, NULL, &hints, &aiptr); if (retval != EINTR && retval != EAI_AGAIN) break; sleep (1); } if (retval) { if (retval == EAI_NONAME) sprintf (nres[idx], "[unknown]"); else if (retval == EAI_FAIL || retval == EAI_AGAIN) sprintf (nres[idx], "[failed]"); else sprintf (nres[idx], "[%s]", gai_strerror(retval)); return (nres[idx]); } else { if (aiptr->ai_family == AF_INET) { /* IPv4 */ addptr = &((struct sockaddr_in *)aiptr->ai_addr)->sin_addr; if (Ip4Addr) memcpy (Ip4Addr, addptr, 4); } else { /* must be IPv6 */ addptr = &((struct sockaddr_in6 *)aiptr->ai_addr)->sin6_addr; if (Ip6Addr) memcpy (Ip6Addr, addptr, 16); } if (!inet_ntop (aiptr->ai_family, addptr, nres[idx], sizeof(nres[0]))) sprintf (nres[idx], "[%s]", strerror(errno)); } /* free the addrinfo */ freeaddrinfo(aiptr); return (nres[idx]); } if (Address) { for (idx = 0; idx < CACHE_MAX; idx++) if (!strcmp (abuf[idx], Address)) return (ares[idx]); idx = aCacheIdx++ % CACHE_MAX; strcpy (abuf[idx], Address); retval = 0; memset (&addr4, 0, sizeof(addr4)); if (inet_pton (AF_INET, Address, &addr4.sin_addr) > 0) { /* MultiNet does not support BSD 4.4 AF_INET addresses */ #ifdef NO_SOCKADDR_LEN_4 /* this kludge seems to work for both! */ *(USHORTPTR)&addr4 = AF_INET; #else addr4.sin_len = sizeof(struct sockaddr_in); addr4.sin_family = AF_INET; #endif if (Ip4Addr) memcpy (Ip4Addr, &addr4.sin_addr, sizeof(addr4.sin_addr)); for (retry = TCPIP_LOOKUP_HOST_NAME_RETRY; retry; retry--) { retval = getnameinfo ((struct sockaddr*)&addr4, sizeof(addr4), ares[idx], sizeof(ares[0]), NULL, 0, NI_NAMEREQD); if (retval != EINTR && retval != EAI_AGAIN) break; sleep (1); } if (retval) { if (retval == EAI_NONAME) sprintf (ares[idx], "[unknown]"); else if (retval == EAI_FAIL || retval == EAI_AGAIN) sprintf (ares[idx], "[failed]"); else sprintf (ares[idx], "[%s]", gai_strerror(retval)); } return (ares[idx]); } else { memset (&addr6, 0, sizeof(addr6)); if (inet_pton (AF_INET6, Address, &addr6.sin6_addr) > 0) { addr6.sin6_len = sizeof(struct sockaddr_in6); addr6.sin6_family = AF_INET6; if (Ip6Addr) memcpy (Ip6Addr, &addr6.sin6_addr, sizeof(addr6.sin6_addr)); for (retry = TCPIP_LOOKUP_HOST_NAME_RETRY; retry; retry--) { retval = getnameinfo ((struct sockaddr*)&addr6, addr6.sin6_len, ares[idx], sizeof(ares[0]), NULL, 0, NI_NAMEREQD); if (retval != EINTR && retval != EAI_AGAIN) break; sleep (1); } if (retval) { if (retval == EAI_NONAME) sprintf (ares[idx], "[unknown]"); else if (retval == EAI_FAIL || retval == EAI_AGAIN) sprintf (ares[idx], "[failed]"); else sprintf (ares[idx], "[%s]", gai_strerror(retval)); } } else sprintf (ares[aCacheIdx], "[%s]", strerror(errno)); return (ares[idx]); } } return ("[bugcheck]"); } /*****************************************************************************/ /* Return true if it looks like an IPv4 or IPv6 address. */ BOOL TcpIpIsAddress (char *string) { char *cptr; /*********/ /* begin */ /*********/ if (dbug) fprintf (stdout, "TcpIpIsAddress() |%s|\n", string); if (!string) return (false); for (cptr = string; *cptr && isspace(*cptr); cptr++); if (!*cptr) return (false); cptr = string; if (*(unsigned long*)cptr == '::FF' && !memcmp (cptr, "::FFFF:", 7)) cptr += 7; else if (*(unsigned long*)cptr == '::ff' && !memcmp (cptr, "::ffff:", 7)) cptr += 7; while (*cptr && (isdigit(*cptr) || *cptr == '.')) cptr++; if (!*cptr) return (true); for (cptr = string; *cptr && (isxdigit(*cptr) || *cptr == ':' || *cptr == '-'); cptr++); if (!*cptr) return (true); return (false); } /*****************************************************************************/ /* Make a delta binary time into an null-terminated ASCII string, absorbing non-significant leading characters. Up to four concurrent times are stored in static storage. */ char* MungeTime (int64 *time64ptr) { #undef BUFFER_MAX #define BUFFER_MAX 4 static int BufferIdx; static char Buffer [BUFFER_MAX][32]; static $DESCRIPTOR (BufferDsc, Buffer[0]); static $DESCRIPTOR (TimeFaoDsc, "!%D\0"); char *cptr, *sptr; /*********/ /* begin */ /*********/ if (dbug) fprintf (stdout, "MungeTime()\n"); BufferDsc.dsc$a_pointer = Buffer[BufferIdx++ % BUFFER_MAX]; if (*time64ptr == 0) strcpy (BufferDsc.dsc$a_pointer, "0.00"); else { sys$fao (&TimeFaoDsc, 0, &BufferDsc, time64ptr); for (cptr = sptr = BufferDsc.dsc$a_pointer; (*cptr == ' ' || *cptr == '0' || *cptr == ':') && cptr - sptr <= 11; cptr++); while (*cptr && *cptr != '.') *sptr++ = *cptr++; *sptr = '\0'; } return (BufferDsc.dsc$a_pointer); } /*****************************************************************************/ /* See explanations in [SRC.HTTPD]REQUEST.C RequestHttpStatusIndex() and RequestHttpStatusCode(). Returns the count of the HTTP response status code. */ int HttpStatusCount (int StatusCode) { int index, StatusGroup, StatusMod; /*********/ /* begin */ /*********/ StatusGroup = StatusCode / 100; if (StatusGroup < 0 || StatusGroup > 5) StatusGroup = 0; /* constrain to 0..599 */ if (StatusCode < 100 || StatusCode > 599) StatusCode = 0; /* constrain to n00..n29 */ if (!StatusCode || (StatusMod = (StatusCode % (StatusGroup * 10))) > 29) StatusCode = StatusMod = 0; /* generates an index from 0..179 */ return (accptr->ResponseStatusCodeCount[((StatusGroup * 30) + StatusMod)]); } /*****************************************************************************/ /* */ void SetInterval (void) { static int64 SecondsDelta; static ulong EfnTmr; int status; /*********/ /* begin */ /*********/ SystemData.PrevMinute = CurrentTime7[4]; /* one to sixty seconds, just multiply by interval */ if (!SecondsDelta) SecondsDelta = RefreshInterval * -10000000; if (!EfnTmr) { status = lib$get_ef (&EfnTmr); if (VMSnok (status)) EXIT_FI_LI (status); } status = sys$setimr (EfnTmr, &SecondsDelta, ProvideData, NULL, 0); if (VMSnok (status)) EXIT_FI_LI (status); } /*****************************************************************************/ /* Get the HTTPd server data from its global section. Data is written by UpdateGlobalSection() in [SRC.HTTPD]SUPPORT.C module. */ void MapGlobalSection (void) { /* system global section, map into first available virtual address */ static int MapFlags = SEC$M_SYSGBL | SEC$M_EXPREG; /* it is recommended to map into any virtual address in the region (P0) */ static ulong InAddr [2] = { 0x200, 0x200 }; static ulong RetAddr [2]; static char HttpdGblSecName [32]; static $DESCRIPTOR (HttpdGblSecNameDsc, HttpdGblSecName); static $DESCRIPTOR (HttpdGblSecNameFaoDsc, GBLSEC_NAME_FAO); int status, ByteSize, PageSize; ushort ShortLength; /*********/ /* begin */ /*********/ if (dbug) fprintf (stdout, "MapGlobalSection()\n"); /* detect change of server PID in case global section also deleted */ if (HttpdGblSecPtr) { /* delete global section virtual addresses and remap */ status = sys$deltva (&RetAddr, 0, 0); if (VMSnok (status)) { fprintf (stdout, "Status: 500\r\n\r\n$DELTVA() %%%08.08X\n", status); exit (SS$_NORMAL); } HttpdGblSecPtr = NULL; } if (!HttpdGblSecPtr) { /* only need to map it the one time */ sys$fao (&HttpdGblSecNameFaoDsc, &ShortLength, &HttpdGblSecNameDsc, HTTPD_NAME, HTTPD_GBLSEC_VERSION_NUMBER, InstanceEnvNumber, "HTTPD"); HttpdGblSecNameDsc.dsc$w_length = ShortLength; if (dbug) fprintf (stdout, "|%s|\n", HttpdGblSecName); /* map the specified global section */ status = sys$mgblsc (&InAddr, &RetAddr, 0, MapFlags, &HttpdGblSecNameDsc, 0, 0); if (dbug) fprintf (stdout, "sys$mgblsc() %%X%08.08X begin:%d end:%d\n", status, RetAddr[0], RetAddr[1]); /* if monitor previously has been running then just wait */ if (HttpdPid) return; if (VMSnok (status)) EXIT_FI_LI(status); ByteSize = (RetAddr[1]+1) - RetAddr[0]; PageSize = (RetAddr[1]+1) - RetAddr[0] >> 9; HttpdGblSecPtr = (HTTPD_GBLSEC*)RetAddr[0]; HttpdGblSecLength = ByteSize; } /* but we do need to check it's the right one before each data read */ if (HttpdGblSecPtr->GblSecVersion != HTTPD_GBLSEC_VERSION_NUMBER || HttpdGblSecPtr->GblSecLength != sizeof(HTTPD_GBLSEC)) { fprintf (stdout, "Status: 500\r\n\r\n\ Global section mismatch! Rebuild ALAMODE?\n"); exit (SS$_NORMAL); } gblptr = HttpdGblSecPtr; accptr = &gblptr->Accounting; pacptr = &gblptr->ProxyAccounting; HttpdPid = gblptr->HttpdProcessId; } /*****************************************************************************/ /* */ int CheckHttpdPid (void) { static unsigned long JpiPid; static struct { short BufferLength; short ItemCode; void *BufferPtr; void *LengthPtr; } JpiItems [] = { { sizeof(JpiPid), JPI$_PID, &JpiPid, 0 }, {0,0,0,0} }; /*********/ /* begin */ /*********/ if (dbug) fprintf (stdout, "CheckHttpdPid() %08.08X\n", HttpdPid); return (sys$getjpiw (EfnWait, &HttpdPid, 0, &JpiItems, 0, 0, 0)); } /*****************************************************************************/ /* */ int HavePriv (ulong *PrivMask) { static ulong JpiImagPriv [2], JpiProcPriv [2]; static struct { short BufferLength; short ItemCode; void *BufferPtr; void *LengthPtr; } JpiItemList[] = { { sizeof(JpiImagPriv), JPI$_IMAGPRIV, &JpiImagPriv, 0 }, { sizeof(JpiProcPriv), JPI$_PROCPRIV, &JpiProcPriv, 0 }, {0,0,0,0} }; int status; /*********/ /* begin */ /*********/ if (dbug) fprintf (stdout, "HavePriv() %08.08X%08.08X\n", PrivMask[0], PrivMask[1]); status = sys$getjpiw (EfnWait, 0, 0, &JpiItemList, 0, 0, 0); if (VMSnok (status)) EXIT_FI_LI(status); return ((((JpiImagPriv[0] & PrivMask[0]) == PrivMask[0]) && ((JpiImagPriv[1] & PrivMask[1]) == PrivMask[1])) || (((JpiProcPriv[0] & PrivMask[0]) == PrivMask[0]) && ((JpiProcPriv[1] & PrivMask[1]) == PrivMask[1]))); } /*****************************************************************************/ /* 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. 'IndexValue' should be zero for a 'flat' logical name, or 0..127 for interative translations. */ char* SysTrnLnm2 ( char *LogName, char *LogValue, int IndexValue ) { static ushort ValueLength; static ulong LnmAttributes, LnmIndex; static char StaticLogValue [256]; static $DESCRIPTOR (LogNameDsc, ""); static $DESCRIPTOR (LnmTableDsc, "LNM$FILE_DEV"); static struct { short int buf_len; short int item; void *buf_addr; ushort *ret_len; } LnmItems [] = { { sizeof(LnmIndex), LNM$_INDEX, &LnmIndex, 0 }, { sizeof(LnmAttributes), LNM$_ATTRIBUTES, &LnmAttributes, 0 }, { 255, LNM$_STRING, 0, &ValueLength }, { 0,0,0,0 } }; int status; char *cptr; /*********/ /* begin */ /*********/ LnmIndex = IndexValue; LogNameDsc.dsc$a_pointer = LogName; LogNameDsc.dsc$w_length = strlen(LogName); if (LogValue) cptr = LnmItems[2].buf_addr = LogValue; else cptr = LnmItems[2].buf_addr = StaticLogValue; status = sys$trnlnm (0, &LnmTableDsc, &LogNameDsc, 0, &LnmItems); if (!(status & 1) || !(LnmAttributes & LNM$M_EXISTS)) { if (LogValue) LogValue[0] = '\0'; return (NULL); } cptr[ValueLength] = '\0'; return (cptr); } /*****************************************************************************/ /* Control debug characteristics to allow use in a CGIplus environment. Set to record if debug enabled or binary if not. */ void Setdbug (int dbugOn) { /*********/ /* begin */ /*********/ if (dbug) fprintf (stdout, "Setdbug()\n"); if (!dbugOn) { dbug = 0; return; } if (dbug) return; if (getenv ("ALAMODE$DBUG")) dbug = 1; if (dbug) fprintf (stdout, "Status: 200\n\ Content-Type: text/plain\n\ Script-Control: X-content-encoding-gzip=0\n\ \r\n"); if (dbug) stdout = freopen ("SYS$OUTPUT:", "w", stdout, "ctx=rec"); else stdout = freopen ("SYS$OUTPUT:", "w", stdout, "ctx=bin"); } /*****************************************************************************/ /* Return true or false depending on whether the server is an equal or later release to the supplied version string. */ int MinimumWASD (char *vstring) { int major[2], minor[2], tweak[2]; char *cptr; /*********/ /* begin */ /*********/ if (dbug) fprintf (stdout, "MinimumWASD() %s\n", vstring); major[0] = minor[0] = tweak[0] = major[1] = minor[1] = tweak[1] = 0; if (sscanf (vstring, "%d.%d.%d", &major[0], &minor[0], &tweak[0]) < 3) return (0); cptr = WsLibCgiVar("SERVER_SOFTWARE"); if (dbug) fprintf (stdout, "%s\n", cptr); if (strstr (cptr, "Apache")) return (0); if (strstr (cptr, "OSU")) return (0); /* if not obvious just bail out */ if (!(cptr = strstr (cptr, "WASD/"))) return (0); /* if doesn't make sense just assume the op knows what they're doing */ if (sscanf (cptr+5, "%d.%d.%d", &major[1], &minor[1], &tweak[1]) < 3) return (1); if (major[1] > major[0]) return (1); if (major[1] == major[0]) { if (minor[1] > minor[0]) return (1); if (minor[1] == minor[0] && tweak[1] >= tweak[0]) return (1); } return (0); } /*****************************************************************************/ /* If WASD and the version >= 10.1 then WebSocket is potentially available. The multi-valued logical name MONDESI_WEBSOCKET allows this to be overridden by defining the first value to be zero, or one. Further, additional values can represent partial strings from the user-agent field. A match returns the complement of the first value. So, 0,"Firefox" would disable WebSocket for all but user-agents containing the string "Firefox". If the leading character is '!' then negates the match, so 0,"!Windows NT","Firefox" would disable WebSocket for all but user-agents containing the string "Firefox" unless it also contained the string "Windows NT". Could be used with the first value defined as 1, in which case it would function to exclude usage by specified WebSocket agent strings. With CGIplus-enabled WebSocket, as it is directly accessing a CGI variable, can only be used before the request setup has completed and the asynchronous WebSocket I/O has begun. Bit of a kludge but allows the flexibility currently required. */ int WebSockAvailable () { int cnt, idx, status, WebSocketOk = FALSE; char *cptr; /*********/ /* begin */ /*********/ if (dbug) fprintf (stdout, "WebSockAvailable()\n"); if (MinimumWASD ("10.1.0")) WebSocketOk = TRUE; for (idx = 0; idx <= 127; idx++) { cptr = SysTrnLnm2 (LOGNAM_WEBSOCKET, NULL, idx); if (!cptr) break; if (cptr[0] == '!') { if (strstr (CgiHttpUserAgentPtr, cptr+1)) { WebSocketOk = !WebSocketOk; break; } } else if (strstr (CgiHttpUserAgentPtr, cptr)) break; } if (dbug) fprintf (stdout, "WebSockOk: %d\n", WebSocketOk); return (WebSocketOk); } /*****************************************************************************/ /* Provide a callout to the server. Must be a '!' (no response callout). */ void ScriptCallout (char *fmt, ...) { int retval; char *cptr; va_list ap; /* must be received as records */ fflush (stdout); fprintf (stdout, "%s\n", SysTrnLnm2("CGIPLUSESC", NULL, 0)); fflush (stdout); va_start (ap, fmt); retval = vfprintf (stdout, fmt, ap); va_end (ap); fflush (stdout); fprintf (stdout, "%s\n", SysTrnLnm2("CGIPLUSEOT", NULL, 0)); fflush (stdout); } /*****************************************************************************/