/*****************************************************************************/ /* HTTPdMon.c This utility displays information about a WASD HTTPd server. The information is derived from JPI data about the process, and read from a permanent system global section in(to) which the server keeps the accounting and writes the request data. The /ALERT and /ALERT=ALL parameters ring the bell every time a new request is reported (the connect count increases). The /ALERT=HOST rings the bell every time the host name in the request changes. With /ALERT=PATH[=] when a path matched by the mapping rule "SET /path* ALERT" is detected by the server the bell is rung times (default is 10). The /LOOKUP qualifier (default) attempts to resolve a dotted-decimal host address in the request (if the server's [DNSlookup] configuration directive is disabled). This operation can introduce some latency as name resolution occurs. A trailing "#" indicates this is an address that has been resolved this way. If it cannot be resolved a "?" is appended to the numeric address. The "(n servers)" field keeps track of multiple servers on a node or cluster. When there is a change in the number the previous value is displayed in parentheses after the current and the terminal bell is rung (this can be suppressed with "/ALERT=NOSERVERS"). Of course servers coming and going are only detected if it happens at a frequency greater than the current display interval. QUALIFIERS ---------- /ALERT[=ALL|HOST|PATH[=]|[NO]SERVERS] ring bell for all accesses or just a change in host and/or suppress bell if server count changes /ALL=string set the server-group name (up to 8 characters) /DBUG turns on all "if (Debug)" statements /DEMO demonstration mode (matches that of the HTTPd) /ENV= refer to this WASD environment (2..15) /[NO]GENERAL display general HTTP serving information /HELP display brief usage information /IDENTIFICATION= the specific instance (server) process PID /INTERVAL= number of seconds between display refresh /[NO]LOOKUP resolve dotted-decimal host addresses to names /PLUCK place selected statistics into global DCL symbols /PORT= the IP port number of the server to monitor /[NO]PROCESS display HTTPd process information /[NO]PROXY display proxy serving information /[NO]REQUEST display request information /[NO]STATUS display instance information /REFRESH= synonym for /INTERVAL REQUIRED PRIVILEGES ------------------- WORLD for access to the server process' JPI data SYSPRV for access to the global section containing the server data BUILD DETAILS ------------- See BUILD_HTTPDMON.COM COPYRIGHT --------- Copyright (C) 1996-2018 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 later version. http://www.gnu.org/licenses/gpl.txt VERSION HISTORY (update SOFTWAREVN as well!) --------------- 14-NOV-2017 MGD v2.6.0, instance status data if |WASD_ENV| defined use that in absence /INSTANCE 12-SEP-2015 MGD v2.5.0, WASD v11 and HTTP/2 30-NOV-2014 MGD v2.4.2, accomodate per-instance "current" data 18-JUN-2010 MGD v2.4.1, add proctor, proxy tunnel and WebSocket remove accept and reject to make way for WebSocket 31-OCT-2009 MGD v2.4.0, just for WASD v10 30-AUG-2009 MGD v2.3.9, bytes per second 09-JUL-2009 MGD v2.3.8, /ENV= 15-OCT-2008 MGD v2.3.7, add WebDAV: module item 15-OCT-2008 MGD v2.3.6, pluck server statistics 21-FEB-2008 MGD v2.3.5, add remote user and realm add Request.BytesTxGzipPercent PercentOf() calc percentages of unsigned longs QuadPercentOf() calc percentages of unsigned quads 19-SEP-2006 MGD v2.3.4, Accounting.ResponseStatusCodeCount changes name to Accounting.ResponseStatusCodeGroup PRC: now shows remaining prclm (as do other quotas) 15-JUL-2006 MGD v2.3.3, abbreviate some stats for humungous sites (UMA :-) add status for SUSPEND and instance PASSIVE modes 24-AUG-2005 MGD v2.3.2, only display printable request URI characters 20-JUL-2005 MGD v2.3.1, detect and report global section mismatch 28-AUG-2004 MGD v2.3.0, modifications during HTTP/1.1 support 16-APR-2004 MGD v2.2.0, modifications to support IPv6, binary resource names for instance locks 27-JAN-2004 MGD v2.1.5, connect processing and keep-alive accounting items 23-DEC-2003 MGD v2.1.4, minor conditional mods to support IA64 22-SEP-2003 MGD v2.1.3, line overflow - if necessary suppress '(403:count)' 11-JUN-2003 MGD v2.1.2, ProxyMaintDeviceStats() inline with WASD v8.3 bugfix; ProxyMaintDeviceStats() volume count handling 26-NOV-2002 MGD v2.1.1, abbreviate proxy cache Rx/Tx prevent line overflow 30-JUN-2002 MGD v2.1.0, HTTPd v8.0, NCS, FTP, etc., /IDENTIFICATION= specific instance PID, /ALERT=PATH notifies when "SET /path* ALERT" hit 17-MAR-2002 MGD v2.0.2, CommaNumber() 64 bit kludge for VAX 04-AUG-2001 MGD v2.0.1, provide 'ANSIceos' before a "STATUS:" field 07-JUL-2001 MGD v2.0.0, HTTPd v7.2, uses permanent global section for data, additional/changed accounting and status information 15-OCT-2000 MGD v1.13.0, HTTPd v7.1, DLM server-group "aware", /ALL=, no current process count if $creprc() detached 20-MAY-2000 MGD v1.12.0, HTTPd v7.0, (no real changes) 30-OCT-1999 MGD v1.11.0, HTTPd v6.1, remove NETLIB support 20-JUN-1999 MGD v1.10.0, add proxy percentage based on counts, allow for disks >9GB in space calculations 25-JAN-1999 MGD v1.9.0, HTTPd v6.0, proxy serving 27-OCT-1998 MGD v1.8.0, HTTPd v5.3, (no real changes) 27-AUG-1998 MGD v1.7.0, HTTPd v5.2, (no real changes) 21-JUN-1998 MGD v1.6.0, HTTPd v5.1, alert, and lookup host address to name 14-FEB-1998 MGD v1.5.0, HTTPd v5.0, DECnet, SSL and minor changes 15-OCT-1997 MGD v1.4.0, HTTPd v4.5, cache 21-AUG-1997 MGD v1.3.0, HTTPd v4.4, accounting structure > 255 bytes, duration 23-JUL-1997 MGD v1.2.1, HTTPd v4.3, (no real changes) 14-JUN-1997 MGD v1.2.0, HTTPd v4.2, CGIplus, bugfix; arithmetic trap on AXP systems 01-FEB-1997 MGD v1.1.0, HTTPd v4.0, (no real changes) 01-DEC-1995 MGD v1.0.0, HTTPd v3.0, initial development */ /*****************************************************************************/ /* note that the layout is a different to other WASD software ID strings! */ #define SOFTWAREVN "v2.6.0" #define SOFTWARENM "HTTPDMON" #ifdef __ALPHA # define SOFTWAREID SOFTWARENM " " SOFTWAREVN " AXP" #endif #ifdef __ia64 # define SOFTWAREID SOFTWARENM " " SOFTWAREVN " IA64" #endif #ifdef __VAX # define SOFTWAREID SOFTWARENM " " SOFTWAREVN " VAX" #endif /* standard C header files */ #include #include #include /* VMS related header files */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* this header file contains the accounting structure definitions */ #ifdef THIS_WASD_H /* when field testing and httpdmon.c is included in the server directory */ #include "./wasd.h" #else #include "../httpd/wasd.h" #endif /* Internet-related header files */ #include #include #include #include #define BOOL int #define true 1 #define false 0 #define FI_LI __FILE__, __LINE__ #define VMSok(x) ((x) & STS$M_SUCCESS) #define VMSnok(x) !(((x) & STS$M_SUCCESS)) #define DEFAULT_INTERVAL_SECONDS 2 #define PATH_ALERT_BELL_COUNT 10 char ErrorProxyMaintTooManyDevices [] = "Volume set has too many members.", Utility [] = "HTTPDMON"; #define ControlZ '\x1a' long SysLckMask [QUAD2] = { PRV$M_SYSLCK, 0 }; BOOL ControlY, Debug, DemoMode, DoAlertAll, DoAlertHost, DoAlertPath, DoAlertServers = true, DoGeneralInfo, DoNoGeneralInfo, DoLookupHost = true, DoProcessInfo, DoNoProcessInfo, DoPluck, DoProxyInfo, DoNoProxyInfo, DoRequestInfo, DoNoRequestInfo, DoStatusInfo, DoNoStatusInfo, DoShowHelp; int CliInstancePid, CurrentProcessCount, InstanceCount, InstanceEnvNumber, InstanceStringLength, IntervalSeconds = DEFAULT_INTERVAL_SECONDS, NextInstancePid, PageLength, PathAlertBellRepetition = PATH_ALERT_BELL_COUNT, PrevConnectCount, ServerPort = 80; unsigned short SyiClusterNodes, SyiNodeLength; unsigned long NowTime64 [2]; char CommandLine [256], InstanceString [64], PrevHostName [256], Screen [16384], ServerProcessName [16], SyiNodeName [16]; char *ScrPtr; $DESCRIPTOR (TcpIpDeviceDsc, "UCX$DEVICE"); int HttpdGblSecLength; HTTPD_GBLSEC *HttpdGblSecPtr; ACCOUNTING_STRUCT *AccountingPtr; /* ANSI terminal control sequences */ char ANSIblink [] = "\x1b[5m", ANSIbold [] = "\x1b[1m", ANSIceol [] = "\x1b[K", ANSIceos [] = "\x1b[J", ANSIcls [] = "\x1b[0;0H\x1b[J", ANSIhome [] = "\x1b[0;0H", ANSInormal [] = "\x1b[0m", ANSIreverse [] = "\x1b[7m", ANSIuline [] = "\x1b[4m"; /* required prototypes */ ControlY_AST (); char* BytesPerString (unsigned int); void AddInstanceStatus(); int PercentOf (unsigned long, unsigned long); int QuadPercentOf (unsigned long*, unsigned long*); int SetSymbol (char*, char*, int); char* TcpIpHostCache (char*, int*, int*); char* TcpIpLookupHostName (char*, int, int*); int TcpIpStringAddress (char*, int*, int*); int ThisLongAgo (unsigned long*, char*); char* TimeString (); char* v10orPrev10 (char*, int); /*****************************************************************************/ /* */ int main () { static $DESCRIPTOR (ttDsc, "TT:"); int status, MonitorStatus; char *cptr; struct { unsigned short buf_len; unsigned short item; unsigned char *buf_addr; void *ret_len; } SyiItems [] = { { sizeof(SyiClusterNodes), SYI$_CLUSTER_NODES, &SyiClusterNodes, 0 }, { sizeof(SyiNodeName)-1, SYI$_NODENAME, &SyiNodeName, &SyiNodeLength }, { 0,0,0,0 } }, TtPageItemList [] = { { sizeof(PageLength), DVI$_TT_PAGE, &PageLength, 0 }, { 0, 0, 0, 0 } }; IO_SB IOsb; /*********/ /* begin */ /*********/ GetParameters (); if (Debug) ANSIcls[0] = ANSIhome[0] = ANSIceol[0] = ANSIceos[0] = '\0'; if (DemoMode) InstanceEnvNumber = DEMO_INSTANCE_GROUP_NUMBER; if (!DoGeneralInfo && !DoProcessInfo && !DoRequestInfo && !DoProxyInfo) { if (!DoNoProcessInfo) DoProcessInfo = true; if (!DoNoGeneralInfo) DoGeneralInfo = true; if (!DoNoRequestInfo) DoRequestInfo = true; } if (DoProxyInfo) { if (!DoNoProcessInfo) DoProcessInfo = true; if (!DoGeneralInfo) DoGeneralInfo = false; if (!DoNoRequestInfo) DoRequestInfo = true; } if (DoShowHelp) exit (ShowHelp ()); if (DoPluck) exit (PluckStats ()); if (VMSnok (status = OnControlY (&ControlY_AST))) exit (status); status = sys$getsyi (0, 0, 0, &SyiItems, &IOsb, 0, 0); if (VMSok (status)) status = IOsb.Status; if (VMSnok (status)) exit (status); SyiNodeName[SyiNodeLength] = '\0'; if (Debug) fprintf (stdout, "|%s|\n", SyiNodeName); status = sys$getdviw (0, 0, &ttDsc, &TtPageItemList, &IOsb, 0, 0, 0); if (VMSok (status)) status = IOsb.Status; if (VMSnok (status)) exit (status); /* if WASD_ENV defined then use this value in the absence of a CLI value */ if (!InstanceEnvNumber) { if (cptr = getenv("WASD_ENV")) InstanceEnvNumber = atoi (cptr); else if (DemoMode) InstanceEnvNumber = DEMO_INSTANCE_GROUP_NUMBER; else InstanceEnvNumber = INSTANCE_ENV_NUMBER_DEFAULT; } if (Debug) fprintf (stdout, "env=%d\n", InstanceEnvNumber); MonitorStatus = MonitorHttpd (); if (VMSnok (status = OnControlY (0))) exit (status); exit (MonitorStatus); } /*****************************************************************************/ /* Assign a channel to the terminal device. Create a buffered screenful of information about the HTTPd server and output it in one IO. */ int MonitorHttpd () { int cnt, status, FillLeft, FillRight, FillRequired, PortLength, InstanceInformationLength, SoftwareIDLength, TimeStringLength; unsigned short TTChannel; char ReadBuffer; char Line [128], Port [16]; char *TimeStringPtr; $DESCRIPTOR (TTDsc, "TT:"); struct { unsigned short Status; unsigned short Offset; unsigned short Terminator; unsigned short TerminatorSize; } IOsb; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "MonitorHttpd()\n"); if (VMSnok (status = sys$assign (&TTDsc, &TTChannel, 0, 0, 0))) return (status); if (VMSnok (status = sys$qiow (0, TTChannel, IO$_WRITELBLK, 0, 0, 0, ANSIcls, sizeof(ANSIcls)-1, 0, 0, 0, 0))) return (status); sprintf (ServerProcessName, "WASD:%d", ServerPort); PortLength = sprintf (Port, "Port: %d", ServerPort); SoftwareIDLength = strlen(SOFTWAREID); for (;;) { if (ControlY) break; sys$gettim (&NowTime64); GetInstanceInformation(); TimeStringPtr = TimeString (); TimeStringLength = strlen(TimeStringPtr); FillRequired = 65 - InstanceStringLength - SoftwareIDLength - TimeStringLength; FillLeft = FillRight = FillRequired / 2; if (FillLeft + FillRight < FillRequired) FillRight++; for (cnt = 6; cnt > SyiNodeLength; cnt--) FillRight--; ScrPtr = Screen; ScrPtr += sprintf (ScrPtr, "%s %*s%s%s::%s %s %*s%s%s%s%*s %s%s%s%s\r\n", ANSIhome, 6 - SyiNodeLength, "", ANSIbold, SyiNodeName, ANSInormal, InstanceString, FillLeft, "", ANSIuline, SOFTWAREID, ANSInormal, FillRight, "", ANSIbold, TimeStringPtr, ANSInormal, ANSIceol); /* we need the startup count and last exit status from count date */ if (VMSnok (status = MapGlobalSection ())) return (status); AccountingPtr = &HttpdGblSecPtr->Accounting; if (HttpdGblSecPtr->GblSecVersion != HTTPD_GBLSEC_VERSION_NUMBER) ScrPtr += sprintf (ScrPtr, "%s %s GLOBAL SECTION MISMATCH! \ (HTTPDMON:%08.08X WASD:%08.08X) %s%s\x07", ANSIceol, ANSIreverse, HTTPD_GBLSEC_VERSION_NUMBER, HttpdGblSecPtr->GblSecVersion, ANSInormal, ANSIceol); if (DoProcessInfo) if (VMSnok (status = AddProcessInfo ())) return (status); if (DoGeneralInfo) if (VMSnok (status = AddGeneralInfo ())) return (status); if (DoProxyInfo) if (VMSnok (status = AddProxyInfo ())) return (status); if (HttpdGblSecPtr->StatusMessage[0]) if (VMSnok (status = AddStatusMessage ())) return (status); else; else if (DoRequestInfo) if (VMSnok (status = AddRequest ())) return (status); if (!DoNoStatusInfo) AddInstanceStatus (); strcpy (ScrPtr, ANSIceos); ScrPtr += sizeof(ANSIceos)-1; if (Debug) fprintf (stdout, "%d bytes\f", ScrPtr-Screen); if (VMSnok (status = sys$qiow (0, TTChannel, IO$_WRITELBLK, 0, 0, 0, Screen, ScrPtr-Screen, 0, 0, 0, 0))) return (status); status = sys$qiow (0, TTChannel, IO$_READLBLK | IO$M_TIMED, &IOsb, 0, 0, &ReadBuffer, 1, IntervalSeconds, 0, 0, 0); if (status == SS$_TIMEOUT) continue; if (VMSnok (status)) return (status); if (IOsb.Terminator == ControlZ) return (SS$_NORMAL); } if (VMSnok (status = sys$qiow (0, TTChannel, IO$_WRITELBLK, 0, 0, 0, Screen, ScrPtr-Screen, 0, 0, 0, 0))) return (status); return (status); } /*****************************************************************************/ /* Get the HTTPd server data from its global section. Data is written by UpdateGlobalSection() in [SRC.HTTPD]SUPPORT.C module. */ int MapGlobalSection () { /* 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 unsigned long InAddr [2] = { 0x200, 0x200 }; static char HttpdGblSecName [32]; static $DESCRIPTOR (HttpdGblSecNameDsc, HttpdGblSecName); static $DESCRIPTOR (HttpdGblSecNameFaoDsc, GBLSEC_NAME_FAO); int status, ByteSize, PageSize; unsigned short ShortLength; unsigned long RetAddr [2]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "MapGlobalSection()\n"); 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 (Debug) fprintf (stdout, "|%s|\n", HttpdGblSecName); /* map the specified global section */ status = sys$mgblsc (&InAddr, &RetAddr, 0, MapFlags, &HttpdGblSecNameDsc, 0, 0); if (Debug) fprintf (stdout, "sys$mgblsc() %%X%08.08X begin:%d end:%d\n", status, RetAddr[0], RetAddr[1]); if (VMSok (status)) { ByteSize = (RetAddr[1]+1) - RetAddr[0]; PageSize = (RetAddr[1]+1) - RetAddr[0] >> 9; HttpdGblSecPtr = (HTTPD_GBLSEC*)RetAddr[0]; HttpdGblSecLength = ByteSize; } else { HttpdGblSecPtr = NULL; HttpdGblSecLength = ByteSize = PageSize = 0; if (status == SS$_NOSUCHSEC) { fprintf (stdout, "%%%s-E-SERVER, no such server!\n\ -SYSTEM-E-NOSUCHSEC, no such (global) section\n", Utility); exit (STS$K_ERROR | STS$M_INHIB_MSG); } else exit (status); } } if (HttpdGblSecPtr->GblSecVersion != HTTPD_GBLSEC_VERSION_NUMBER || HttpdGblSecPtr->GblSecLength != sizeof(HTTPD_GBLSEC)) { fprintf (stdout, "%%%s-E-GBLSEC, global section mismatch, rebuild HTTPDMON?\n", Utility); exit (STS$K_ERROR | STS$M_INHIB_MSG); } return (SS$_NORMAL); } /*****************************************************************************/ /* Add the HTTPd server process information to the screen buffer using the process ID from the PID logical and a a sys$getjpi() call. The PID logical contains binary data in the form of a longword process ID. */ int AddProcessInfo () { static char JpiPrcNam [16], JpiUserName [13], UpTime [32]; static unsigned long JpiAstCnt, JpiAstLm, JpiBioCnt, JpiBytLm, JpiBytCnt, JpiBioLm, JpiCpuTime, JpiDioCnt, JpiDioLm, JpiEnqCnt, JpiEnqLm, JpiFilCnt, JpiFilLm, JpiPageFlts, JpiPagFilCnt, JpiPgFlQuota, JpiPid, JpiPrcCnt, JpiPrcLm, JpiTqCnt, JpiTqLm, JpiVirtPeak, JpiWsSize, JpiWsPeak; static unsigned long ConnectTime [QUAD2], JpiLoginTime [QUAD2]; static char LastExitStatus [48] = ""; static $DESCRIPTOR (UpTimeFaoDsc, "!%D"); static $DESCRIPTOR (UpTimeDsc, UpTime); static struct { unsigned short buf_len; unsigned short item; unsigned char *buf_addr; void *ret_len; } JpiItems [] = { { sizeof(JpiAstCnt), JPI$_ASTCNT, &JpiAstCnt, 0 }, { sizeof(JpiAstLm), JPI$_ASTLM, &JpiAstLm, 0 }, { sizeof(JpiBioCnt), JPI$_BIOCNT, &JpiBioCnt, 0 }, { sizeof(JpiBioLm), JPI$_BIOLM, &JpiBioLm, 0 }, { sizeof(JpiBytCnt), JPI$_BYTCNT, &JpiBytCnt, 0 }, { sizeof(JpiBytLm), JPI$_BYTLM, &JpiBytLm, 0 }, { sizeof(JpiCpuTime), JPI$_CPUTIM, &JpiCpuTime, 0 }, { sizeof(JpiDioCnt), JPI$_DIOCNT, &JpiDioCnt, 0 }, { sizeof(JpiDioLm), JPI$_DIOLM, &JpiDioLm, 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(JpiLoginTime), JPI$_LOGINTIM, &JpiLoginTime, 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(JpiPrcCnt), JPI$_PRCCNT, &JpiPrcCnt, 0 }, { sizeof(JpiPrcLm), JPI$_PRCLM, &JpiPrcLm, 0 }, { sizeof(JpiPrcNam), JPI$_PRCNAM, &JpiPrcNam, 0 }, { sizeof(JpiTqCnt), JPI$_TQCNT, &JpiTqCnt, 0 }, { sizeof(JpiTqLm), JPI$_TQLM, &JpiTqLm, 0 }, { sizeof(JpiUserName), JPI$_USERNAME, &JpiUserName, 0 }, { sizeof(JpiVirtPeak), JPI$_VIRTPEAK, &JpiVirtPeak, 0 }, { sizeof(JpiWsSize), JPI$_WSSIZE, &JpiWsSize, 0 }, { sizeof(JpiWsPeak), JPI$_WSPEAK, &JpiWsPeak, 0 }, {0,0,0,0} }; int status, col1, col2, col3, col4, colen, len1, len2; unsigned short Length; unsigned long HttpdPid; char *cptr, *StatePtr, *UpTimePtr; char str1 [32], str2 [32], AstString [32], BioString [32], BytString [32], DioString [32], EnqString [32], FilString [32], PrcString [32], TqString [32]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "AddProcessInfo()\n"); if (!(HttpdPid = CliInstancePid)) if (!(HttpdPid = NextInstancePid)) if (!(HttpdPid = HttpdGblSecPtr->HttpdProcessId)) exit (SS$_BUGCHECK); status = sys$getjpiw (0, &HttpdPid, 0, &JpiItems, 0, 0, 0); if (Debug) fprintf (stdout, "sys$getjpi() %%X%08.08X\n", status); if (VMSnok (status) || status == SS$_NONEXPR) { if (status == SS$_NONEXPR) StatePtr = "NONEXPR"; else if (status == SS$_SUSPENDED) StatePtr = "MWAIT"; else return (status); } else StatePtr = NULL; CurrentProcessCount = JpiPrcCnt; JpiUserName[12] = '\0'; for (cptr = JpiUserName; *cptr && *cptr != ' '; cptr++); *cptr = '\0'; if (Debug) fprintf (stdout, "JpiUserName |%s|\n", JpiUserName); JpiPrcNam[15] = '\0'; for (cptr = JpiPrcNam; *cptr && *cptr != ' '; cptr++); *cptr = '\0'; if (Debug) fprintf (stdout, "JpiPrcNam |%s|\n", JpiPrcNam); lib$sub_times (&NowTime64, &JpiLoginTime, &ConnectTime); sys$fao (&UpTimeFaoDsc, &Length, &UpTimeDsc, &ConnectTime); UpTime[Length] = '\0'; for (UpTimePtr = UpTime; isspace(*UpTimePtr); UpTimePtr++); if (AccountingPtr->LastExitStatus) sprintf (LastExitStatus, " %sExit:%s %%X%08.08X", ANSIbold, ANSInormal, AccountingPtr->LastExitStatus); len1 = sprintf (str1, "%d", JpiAstCnt); len2 = sprintf (str2, "%d", JpiEnqCnt); if (len2 > len1) len1 = len2; else len2 = len1; col1 = sprintf (AstString, "%*.*s/%d", len1, len1, str1, JpiAstLm); colen = sprintf (EnqString, "%*.*s/%d", len2, len2, str2, JpiEnqLm); if (colen > col1) col1 = colen; len1 = sprintf (str1, "%d", JpiBioCnt); len2 = sprintf (str2, "%d", JpiFilCnt); if (len2 > len1) len1 = len2; else len2 = len1; col2 = sprintf (BioString, "%*.*s/%d", len1, len1, str1, JpiBioLm); colen = sprintf (FilString, "%*.*s/%d", len2, len2, str2, JpiFilLm); if (colen > col2) col2 = colen; len1 = sprintf (str1, "%d", JpiBytCnt); len2 = sprintf (str2, "%d", JpiPrcLm-JpiPrcCnt); if (len2 > len1) len1 = len2; else len2 = len1; col3 = sprintf (BytString, "%*.*s/%d", len1, len1, str1, JpiBytLm); colen = sprintf (PrcString, "%*.*s/%d", len2, len2, str2, JpiPrcLm); if (colen > col3) col3 = colen; len1 = sprintf (str1, "%d", JpiDioCnt); len2 = sprintf (str2, "%d", JpiTqCnt); if (len2 > len1) len1 = len2; else len2 = len1; col4 = sprintf (DioString, "%*.*s/%d", len1, len1, str1, JpiDioLm); colen = sprintf (TqString, "%*.*s/%d", len2, len2, str2, JpiTqLm); if (colen > col4) col4 = colen; ScrPtr += sprintf (ScrPtr, "%s\r\n\ %sProcess:%s ", ANSIceol, ANSIbold, ANSInormal); if (StatePtr) ScrPtr += sprintf (ScrPtr, "%s %s %s", ANSIreverse, StatePtr, ANSInormal); else ScrPtr += sprintf (ScrPtr, "%s", JpiPrcNam); ScrPtr += sprintf (ScrPtr, " %sPID:%s %08.08X %sUser:%s %s %sVersion:%s %s\ %s\r\n\ %sUp:%s %s %sCPU:%s %d %02.02d:%02.02d:%02.02d.%02.02d\ %sStartup:%s %d%s\ %s\r\n\ %sPg.Flts:%s %d %sPg.Used:%s %d%% %sWsSize:%s %d %sWsPeak:%s %d\ %s\r\n\ %sAST:%s %-*s %sBIO:%s %-*s %sBYT:%s %-*s %sDIO:%s %-*s\ %s\r\n\ %sENQ:%s %-*s %sFIL:%s %-*s %sPRC:%s %-*s %sTQ:%s %-*s\ %s\r\n", ANSIbold, ANSInormal, HttpdPid, ANSIbold, ANSInormal, JpiUserName, ANSIbold, ANSInormal, HttpdGblSecPtr->HttpdVersion, ANSIceol, ANSIbold, ANSInormal, UpTimePtr, ANSIbold, ANSInormal, JpiCpuTime / 8640000, /* CPU day */ (JpiCpuTime % 8640000) / 360000, /* CPU hour */ (JpiCpuTime % 360000) / 6000, /* CPU minute */ (JpiCpuTime % 6000 ) / 100, /* CPU second */ JpiCpuTime % 100, /* CPU 10mS */ ANSIbold, ANSInormal, AccountingPtr->StartupCount, LastExitStatus, ANSIceol, ANSIbold, ANSInormal, JpiPageFlts, ANSIbold, ANSInormal, 100-PercentOf(JpiPagFilCnt,JpiPgFlQuota), ANSIbold, ANSInormal, JpiWsSize, ANSIbold, ANSInormal, JpiWsPeak, ANSIceol, ANSIbold, ANSInormal, col1, AstString, ANSIbold, ANSInormal, col2, BioString, ANSIbold, ANSInormal, col3, BytString, ANSIbold, ANSInormal, col4, DioString, ANSIceol, ANSIbold, ANSInormal, col1, EnqString, ANSIbold, ANSInormal, col2, FilString, ANSIbold, ANSInormal, col3, PrcString, ANSIbold, ANSInormal, col4, TqString, ANSIceol); return (SS$_NORMAL); } /*****************************************************************************/ /* Add the HTTPd server counter information to the screen buffer from the count logical. The information in this logical is binary data in the form of the HTTPd count data structure. This is used to extract each field member from the data. */ int AddGeneralInfo () { static int ErrorsNoticedCount; static char NCScounts [48] = "", WebSockOrNoticed [96]; int idx, status, CodeGroup, ConnectTotalCount, DigitCount, CurrentThrottleProcessing, CurrentThrottleQueued, CurrentWebSockets; char *bpsptr; char BytesRawRx [32], BytesRawTx [32], CurrentProcessingString [128], CurrentProcessCountString [16], Four03s [16]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "AddGeneralInfo()\n"); ConnectTotalCount = AccountingPtr->ConnectIpv4Count + AccountingPtr->ConnectIpv6Count; if (DoAlertAll) if (PrevConnectCount && ConnectTotalCount > PrevConnectCount) fputs ("\x07", stdout); PrevConnectCount = ConnectTotalCount; /* double-up on the real estate */ if (AccountingPtr->ErrorsNoticedCount && AccountingPtr->ErrorsNoticedCount != ErrorsNoticedCount) { sprintf (WebSockOrNoticed, "%sNoticed:%s %d", ANSIbold, ANSInormal, AccountingPtr->ErrorsNoticedCount); ErrorsNoticedCount = AccountingPtr->ErrorsNoticedCount; } else sprintf (WebSockOrNoticed, "%sWS:%s %d/%d%% %sBusy:%s %d", ANSIbold, ANSInormal, AccountingPtr->DclWebSocketCount, PercentOf(AccountingPtr->DclWebSocketCount, AccountingPtr->ProcessingTotalCount[HTTP12]), ANSIbold, ANSInormal, AccountingPtr->ConnectTooBusyCount); if (AccountingPtr->NcsConvertCount) sprintf (NCScounts, " %sNCS:%s %d/%d", ANSIbold, ANSInormal, AccountingPtr->NcsCount, AccountingPtr->NcsConvertCount); DigitCount = 0; for (idx = 0; idx <= 5; idx++) { if (idx == 1) continue; CodeGroup = AccountingPtr->ResponseStatusCodeGroup[idx]; DigitCount++; while (CodeGroup /= 10) DigitCount++; } CodeGroup = AccountingPtr->RequestForbiddenCount; DigitCount++; while (CodeGroup /= 10) DigitCount++; if (DigitCount > 35) Four03s[0] = '\0'; else if (DigitCount > 31) sprintf (Four03s, " (%d)", AccountingPtr->RequestForbiddenCount); else sprintf (Four03s, " (403:%d)", AccountingPtr->RequestForbiddenCount); CommaNumber (64, &AccountingPtr->BytesRawRx, BytesRawRx); CommaNumber (64, &AccountingPtr->BytesRawTx, BytesRawTx); sprintf (CurrentProcessCountString, "/%d", CurrentProcessCount); if (HttpdGblSecPtr->ConnectSuspend) sprintf (CurrentProcessingString, " %s%s S U S P E N D %s%s", ANSIreverse, ANSIbold, ANSInormal, ANSInormal); else if (HttpdGblSecPtr->InstancePassive) sprintf (CurrentProcessingString, " %s%s P A S S I V E %s%s", ANSIreverse, ANSIbold, ANSInormal, ANSInormal); else CurrentProcessingString[0] = '\0'; for (CurrentWebSockets = idx = 0; idx <= HttpdGblSecPtr->InstanceNodeCurrent; idx++) CurrentWebSockets += AccountingPtr->CurrentWebSockets[idx]; for (CurrentThrottleProcessing = idx = 0; idx <= HttpdGblSecPtr->InstanceNodeCurrent; idx++) CurrentThrottleProcessing += AccountingPtr->CurrentThrottleProcessing[idx]; for (CurrentThrottleQueued = idx = 0; idx <= HttpdGblSecPtr->InstanceNodeCurrent; idx++) CurrentThrottleQueued += AccountingPtr->CurrentThrottleQueued[idx]; bpsptr = BytesPerString (AccountingPtr->BytesPerSecondAve); ScrPtr += sprintf (ScrPtr, "%s%s\r\n\ %sRequest:%s %d %sCurrent:%s %d/%d/%d/%d \ %sThrottle:%s %d/%d/%d%% %sPeak:%s %d/%d\ %s\r\n\ %sHTTP/2:%s %d/%d%% %s/1.n:%s %d/%d%% %sSSL:%s %d/%d%% %s\ %s\r\n\ %sCONNECT:%s %d %sGET:%s %d %sHEAD:%s %d %sPOST:%s %d %sPUT:%s %d (%d)\ %s\r\n\ %sAdmin:%s %d %sCache:%s %d/%d/%d %sDECnet:%s %d/%d %sDir:%s %d%s%s\r\n\ %sDCL:%s CGI:%d CGIplus:%d/%d RTE:%d/%d Prc:%d%s Prct:%d\ %s\r\n\ %sFile:%s %d/%d %sProxy:%s %d\ %sPut:%s %d %sSSI:%s %d %sWebDAV:%s %d/%d\ %s\r\n\ %s\r\n\ %s0xx:%s %d %s2xx:%s %d %s3xx:%s %d %s4xx:%s %d%s %s5xx:%s %d\ %s\r\n\ %sRx:%s %s (%d err) %sTx:%s %s (%d err) (%s)\ %s\r\n", CurrentProcessingString, ANSIceol, ANSIbold, ANSInormal, AccountingPtr->ProcessingTotalCount[HTTP12], ANSIbold, ANSInormal, AccountingPtr->CurrentConnected[HTTP12], AccountingPtr->CurrentProcessing[HTTP12], CurrentWebSockets, HttpdGblSecPtr->ProxyAccounting.TunnelCurrent, ANSIbold, ANSInormal, CurrentThrottleProcessing, CurrentThrottleQueued, AccountingPtr->ThrottleBusyMetric, ANSIbold, ANSInormal, AccountingPtr->ConnectPeak[HTTP12], AccountingPtr->ProcessingPeak[HTTP12], ANSIceol, ANSIbold, ANSInormal, AccountingPtr->ProcessingTotalCount[HTTP2], PercentOf(AccountingPtr->ProcessingTotalCount[HTTP2], AccountingPtr->ProcessingTotalCount[HTTP12]), ANSIbold, ANSInormal, AccountingPtr->ProcessingTotalCount[HTTP1], PercentOf(AccountingPtr->ProcessingTotalCount[HTTP1], AccountingPtr->ProcessingTotalCount[HTTP12]), ANSIbold, ANSInormal, AccountingPtr->ConnectSslCount, PercentOf(AccountingPtr->ConnectSslCount, AccountingPtr->ConnectAcceptedCount), WebSockOrNoticed, ANSIceol, ANSIbold, ANSInormal, AccountingPtr->MethodConnectCount, ANSIbold, ANSInormal, AccountingPtr->MethodGetCount, ANSIbold, ANSInormal, AccountingPtr->MethodHeadCount, ANSIbold, ANSInormal, AccountingPtr->MethodPostCount, ANSIbold, ANSInormal, AccountingPtr->MethodPutCount, AccountingPtr->MethodDeleteCount + AccountingPtr->MethodExtensionCount + AccountingPtr->MethodOptionsCount + AccountingPtr->MethodTraceCount, ANSIceol, ANSIbold, ANSInormal, AccountingPtr->DoServerAdminCount, ANSIbold, ANSInormal, AccountingPtr->CacheLoadCount, AccountingPtr->CacheHitCount, AccountingPtr->CacheHitNotModifiedCount, ANSIbold, ANSInormal, AccountingPtr->DoDECnetCgiCount, AccountingPtr->DoDECnetOsuCount, ANSIbold, ANSInormal, AccountingPtr->DoDirectoryCount, NCScounts, ANSIceol, ANSIbold, ANSInormal, AccountingPtr->DoScriptCount, AccountingPtr->DoCgiPlusScriptCount, AccountingPtr->DclCgiPlusReusedCount, AccountingPtr->DoRteScriptCount, AccountingPtr->DclRteReusedCount, AccountingPtr->DclCrePrcCount, CurrentProcessCountString, AccountingPtr->DclProctorCount, ANSIceol, ANSIbold, ANSInormal, AccountingPtr->DoFileCount, AccountingPtr->DoFileNotModifiedCount, ANSIbold, ANSInormal, AccountingPtr->DoProxyCount, ANSIbold, ANSInormal, AccountingPtr->DoPutCount, ANSIbold, ANSInormal, AccountingPtr->DoSsiCount, ANSIbold, ANSInormal, AccountingPtr->DoWebDavCount, AccountingPtr->MethodWebDavCopyCount + AccountingPtr->MethodWebDav_DeleteCount + AccountingPtr->MethodWebDav_GetCount + AccountingPtr->MethodWebDavLockCount + AccountingPtr->MethodWebDavMkColCount + AccountingPtr->MethodWebDavMoveCount + AccountingPtr->MethodWebDav_OptionsCount + AccountingPtr->MethodWebDavPropFindCount + AccountingPtr->MethodWebDavPropPatchCount + AccountingPtr->MethodWebDav_PutCount + AccountingPtr->MethodWebDavUnLockCount, ANSIceol, ANSIceol, ANSIbold, ANSInormal, AccountingPtr->ResponseStatusCodeGroup[0], ANSIbold, ANSInormal, AccountingPtr->ResponseStatusCodeGroup[2], ANSIbold, ANSInormal, AccountingPtr->ResponseStatusCodeGroup[3], ANSIbold, ANSInormal, AccountingPtr->ResponseStatusCodeGroup[4], Four03s, ANSIbold, ANSInormal, AccountingPtr->ResponseStatusCodeGroup[5], ANSIceol, ANSIbold, ANSInormal, BytesRawRx, AccountingPtr->NetReadErrorCount, ANSIbold, ANSInormal, BytesRawTx, AccountingPtr->NetWriteErrorCount, bpsptr, ANSIceol); return (SS$_NORMAL); } /*****************************************************************************/ /* Add the HTTPd server counter information to the screen buffer from the count logical. The information in this logical is binary data in the form of the HTTPd count data structure. This is used to extract each field member from the data. */ int AddProxyInfo () { static int FreeSpaceAlertCount; int idx, status, ConnectTotalCount, CountNetwork, CountTotal, FreeBlocks, FreeMBytes, PercentBytesCache, PercentBytesNetwork, PercentBytesCannotCache, PercentCountCacheRead, PercentCountCacheWrite, PercentCountRequestCannotCache, PercentCountResponseCannotCache, PercentCountNetwork, ErrorCount, FreePercent, TotalMBytes, TotalBlocks, UsedBlocks, UsedMBytes, UsedPercent; unsigned long BytesCache [QUAD2], BytesCannotCache [QUAD2], BytesRaw [QUAD2], BytesTotal [QUAD2]; char BytesCacheRx [32], BytesCacheTx [32], BytesCannotCacheRx [32], BytesCannotCacheTx [32], BytesRawRx [32], BytesRawTx [32], ConnectString [128]; char *cptr, *sptr, *DevNamePtr, *EnabledPtr, *SpaceAvailablePtr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "AddProxyInfo()\n"); ConnectTotalCount = AccountingPtr->ConnectIpv4Count + AccountingPtr->ConnectIpv6Count; if (!DoGeneralInfo) { if (DoAlertAll) if (PrevConnectCount && ConnectTotalCount > PrevConnectCount) fputs ("\x07", stdout); PrevConnectCount = ConnectTotalCount; } if (HttpdGblSecPtr->ProxyAccounting.ServingEnabled) EnabledPtr = "enabled"; else EnabledPtr = "DISABLED"; if (HttpdGblSecPtr->ProxyAccounting.FreeSpaceAvailable) { SpaceAvailablePtr = "available"; FreeSpaceAlertCount = 0; } else { SpaceAvailablePtr = "\x1b[1mUNAVAILABLE\x1b[0m"; if (!FreeSpaceAlertCount--) { /* alert no free space every minute or so */ if (IntervalSeconds >= 60) FreeSpaceAlertCount = 1; else FreeSpaceAlertCount = 60 / IntervalSeconds; } } ProxyMaintDeviceStats (&DevNamePtr, &TotalBlocks, &UsedBlocks, &FreeBlocks, &ErrorCount); TotalMBytes = TotalBlocks >> 11; UsedMBytes = UsedBlocks >> 11; FreeMBytes = FreeBlocks >> 11; FreePercent = PercentOf(FreeBlocks,TotalBlocks); UsedPercent = 100 - FreePercent; /*********/ /* bytes */ /*********/ status = lib$addx (&HttpdGblSecPtr->ProxyAccounting.BytesCacheRx, &HttpdGblSecPtr->ProxyAccounting.BytesCacheTx, &BytesCache, 0); if (Debug) fprintf (stdout, "lib$addx() %%X%08.08X\n", status); status = lib$addx (&HttpdGblSecPtr->ProxyAccounting.BytesCannotCacheRx, &HttpdGblSecPtr->ProxyAccounting.BytesCannotCacheTx, &BytesCannotCache, 0); if (Debug) fprintf (stdout, "lib$addx() %%X%08.08X\n", status); status = lib$addx (&HttpdGblSecPtr->ProxyAccounting.BytesRawRx, &HttpdGblSecPtr->ProxyAccounting.BytesRawTx, &BytesRaw, 0); if (Debug) fprintf (stdout, "lib$addx() %%X%08.08X\n", status); status = lib$addx (&BytesCache, &BytesRaw, &BytesTotal, 0); if (Debug) fprintf (stdout, "lib$addx() %%X%08.08X\n", status); CommaNumber (64, &HttpdGblSecPtr->ProxyAccounting.BytesCacheRx, BytesCacheRx); for (cptr = BytesCacheRx; *cptr && *cptr != ','; cptr++); if (*cptr) cptr++; while (*cptr && *cptr != ',') cptr++; if (*cptr) { sptr = cptr; cptr++; while (*cptr && *cptr != ',') cptr++; if (*cptr) *(unsigned short*)sptr = 'M\0'; else *(unsigned short*)sptr = 'k\0'; } CommaNumber (64, &HttpdGblSecPtr->ProxyAccounting.BytesCacheTx, BytesCacheTx); for (cptr = BytesCacheTx; *cptr && *cptr != ','; cptr++); if (*cptr) cptr++; while (*cptr && *cptr != ',') cptr++; if (*cptr) { sptr = cptr; cptr++; while (*cptr && *cptr != ',') cptr++; if (*cptr) *(unsigned short*)sptr = 'M\0'; else *(unsigned short*)sptr = 'k\0'; } CommaNumber (64, &HttpdGblSecPtr->ProxyAccounting.BytesRawRx, BytesRawRx); CommaNumber (64, &HttpdGblSecPtr->ProxyAccounting.BytesRawTx, BytesRawTx); PercentBytesCache = QuadPercentOf (&BytesTotal, &BytesCache); PercentBytesCannotCache = QuadPercentOf (&BytesTotal, &BytesCannotCache); PercentBytesNetwork = QuadPercentOf (&BytesTotal, &BytesRaw); /**********/ /* counts */ /**********/ CountNetwork = HttpdGblSecPtr->ProxyAccounting.ConnectIpv4Count + HttpdGblSecPtr->ProxyAccounting.ConnectIpv6Count; CountTotal = CountNetwork + HttpdGblSecPtr->ProxyAccounting.CacheReadCount; PercentCountCacheRead = PercentOf (HttpdGblSecPtr->ProxyAccounting.CacheReadCount, CountTotal); PercentCountNetwork = PercentOf (CountNetwork, CountTotal); PercentCountCacheWrite = PercentOf (HttpdGblSecPtr->ProxyAccounting.CacheWriteCount, CountNetwork); PercentCountResponseCannotCache = PercentOf (HttpdGblSecPtr->ProxyAccounting.ResponseCannotCacheCount, CountNetwork); /***********/ /* display */ /***********/ if (DoGeneralInfo) ConnectString[0] = '\0'; else sprintf (ConnectString, " %sConnect:%s Current:%d Peak:%d", ANSIbold, ANSInormal, AccountingPtr->CurrentConnected[HTTP12], AccountingPtr->ConnectPeak[HTTP12]); ScrPtr += sprintf (ScrPtr, "%s\r\n\ %sProxy:%s %s%s\ %s\r\n\ %sCONNECT:%s %d %sGET:%s %d %sHEAD:%s %d \ %sPOST:%s %d %sPUT:%s %d %sOther:%s %d\ %s\r\n\ %sNot:%s cacheable Rx/Tx: %d%% Request:%d (%d%%) Response:%d (%d%%)\ %s\r\n\ %sNetwork:%s Rx:%s Tx:%s (%d%%) Requested:%d (%d%%)\ %s\r\n\ %sLookup:%s Literal:%d DNS:%d Cache:%d Error:%d %sFTP:%s %d\ %s\r\n\ %sCache:%s Rx:%s Tx:%s (%d%%) Rd:%d/%d (%d%%) Wr:%d (%d%%)\ %s\r\n\ %sDevice:%s %s %d blocks (%dMB) %sErrors:%s %d %sSpace:%s %s\ %s\r\n\ %d used (%dMB %d%%), %d free (%dMB %d%%)\ %s\r\n\ %sScan:%s %.69s\ %s\r\n", ANSIceol, ANSIbold, ANSInormal, EnabledPtr, ConnectString, ANSIceol, ANSIbold, ANSInormal, HttpdGblSecPtr->ProxyAccounting.MethodConnectCount, ANSIbold, ANSInormal, HttpdGblSecPtr->ProxyAccounting.MethodGetCount, ANSIbold, ANSInormal, HttpdGblSecPtr->ProxyAccounting.MethodHeadCount, ANSIbold, ANSInormal, HttpdGblSecPtr->ProxyAccounting.MethodPostCount, ANSIbold, ANSInormal, HttpdGblSecPtr->ProxyAccounting.MethodPutCount, ANSIbold, ANSInormal, HttpdGblSecPtr->ProxyAccounting.MethodDeleteCount + HttpdGblSecPtr->ProxyAccounting.MethodExtensionCount + HttpdGblSecPtr->ProxyAccounting.MethodOptionsCount + HttpdGblSecPtr->ProxyAccounting.MethodTraceCount, ANSIceol, ANSIbold, ANSInormal, PercentBytesCannotCache, HttpdGblSecPtr->ProxyAccounting.RequestCannotCacheCount, PercentCountRequestCannotCache, HttpdGblSecPtr->ProxyAccounting.ResponseCannotCacheCount, PercentCountResponseCannotCache, ANSIceol, ANSIbold, ANSInormal, BytesRawRx, BytesRawTx, PercentBytesNetwork, HttpdGblSecPtr->ProxyAccounting.ConnectIpv4Count + HttpdGblSecPtr->ProxyAccounting.ConnectIpv6Count, PercentCountNetwork, ANSIceol, ANSIbold, ANSInormal, AccountingPtr->LookupLiteralCount, AccountingPtr->LookupDnsNameCount, AccountingPtr->LookupCacheNameCount, AccountingPtr->LookupDnsNameErrorCount, ANSIbold, ANSInormal, HttpdGblSecPtr->ProxyAccounting.FtpCount, ANSIceol, ANSIbold, ANSInormal, BytesCacheRx, BytesCacheTx, PercentBytesCache, HttpdGblSecPtr->ProxyAccounting.CacheReadCount, HttpdGblSecPtr->ProxyAccounting.CacheRead304Count, PercentCountCacheRead, HttpdGblSecPtr->ProxyAccounting.CacheWriteCount, PercentCountCacheWrite, ANSIceol, ANSIbold, ANSInormal, DevNamePtr, TotalBlocks, TotalMBytes, ANSIbold, ANSInormal, ErrorCount, ANSIbold, ANSInormal, SpaceAvailablePtr, ANSIceol, UsedBlocks, UsedMBytes, UsedPercent, FreeBlocks, FreeMBytes, FreePercent, ANSIceol, ANSIbold, ANSInormal, HttpdGblSecPtr->ProxyAccounting.StatusString, ANSIceol); return (SS$_NORMAL); } /****************************************************************************/ /* Return information about file system space on the proxy cache device. Will function correctly with volume sets of up to eight members. Returns a VMS status code that should be checked for success. */ int ProxyMaintDeviceStats ( char **DevNamePtrPtr, int *TotalBlocksPtr, int *UsedBlocksPtr, int *FreeBlocksPtr, int *ErrorCountPtr ) { #define PROXY_MAINT_CACHE_DEVICE_MAX 8 static int DeviceCount, ErrCnt, FreeBlocks, MaxBlock, VolCount; static unsigned short Length; static short DevChannel [PROXY_MAINT_CACHE_DEVICE_MAX]; static char CacheDevName [65], DevName [65]; static $DESCRIPTOR (DevNameDsc, ""); static struct { short BufferLength; short ItemCode; void *BufferPtr; void *LengthPtr; } DevNamItemList [] = { { sizeof(CacheDevName), DVI$_DEVNAM, &CacheDevName, &Length }, { sizeof(VolCount), DVI$_VOLCOUNT, &VolCount, 0 }, { 0, 0, 0, 0 } }, NextDevNamItemList [] = { { sizeof(DevName), DVI$_NEXTDEVNAM, &DevName, &Length }, { 0, 0, 0, 0 } }, BlocksItemList [] = { { sizeof(MaxBlock), DVI$_MAXBLOCK, &MaxBlock, 0 }, { sizeof(FreeBlocks), DVI$_FREEBLOCKS, &FreeBlocks, 0 }, { sizeof(ErrCnt), DVI$_ERRCNT, &ErrCnt, 0 }, { 0, 0, 0, 0 } }; int idx, status, ErrorCount, TotalBlocks, TotalFreeBlocks, TotalUsedBlocks; IO_SB IOsb; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ProxyMaintDeviceStats()\n"); if (DevNamePtrPtr) *DevNamePtrPtr = DevName; if (TotalBlocksPtr) *TotalBlocksPtr = 0; if (UsedBlocksPtr) *UsedBlocksPtr = 0; if (FreeBlocksPtr) *FreeBlocksPtr = 0; if (ErrorCountPtr) *ErrorCountPtr = 0; if (!DeviceCount) { /**************/ /* initialize */ /**************/ DevNameDsc.dsc$a_pointer = v10orPrev10(PROXY_CACHE_ROOT,-1); DevNameDsc.dsc$w_length = strlen(DevNameDsc.dsc$a_pointer); /* assign a channel to the cache device (or primary if a volume set) */ if (VMSnok (status = sys$assign (&DevNameDsc, &DevChannel[DeviceCount], 0, 0))) return (status); DeviceCount++; status = sys$getdviw (0, DevChannel[0], 0, &DevNamItemList, &IOsb, 0, 0, 0); if (VMSok (status)) status = IOsb.Status; if (VMSnok (status)) return (status); CacheDevName[Length] = '\0'; if (CacheDevName[0] == '_') memmove (CacheDevName, CacheDevName+1, Length); if (Debug) fprintf (stdout, "|%s| %d\n", CacheDevName, VolCount); /* loop assigning a channel to all devices in volume set (if it is!) */ while (--VolCount) { if (DeviceCount >= PROXY_MAINT_CACHE_DEVICE_MAX) exit (SS$_BUGCHECK); status = sys$getdviw (0, DevChannel[0], 0, &NextDevNamItemList, &IOsb, 0, 0, 0); if (VMSok (status)) status = IOsb.Status; if (VMSnok (status)) return (status); DevName[Length] = '\0'; if (Debug) fprintf (stdout, "|%s|\n", DevName); if (!Length) break; DevNameDsc.dsc$w_length = Length; DevNameDsc.dsc$a_pointer = DevName; if (VMSnok (status = sys$assign (&DevNameDsc, &DevChannel[DeviceCount++], 0, 0))) return (status); } if (Debug) fprintf (stdout, "DeviceCount: %d\n", DeviceCount); } /***********/ /* process */ /***********/ ErrorCount = TotalBlocks = TotalFreeBlocks = 0; for (idx = 0; idx < DeviceCount; idx++) { status = sys$getdviw (0, DevChannel[idx], 0, &BlocksItemList, &IOsb, 0, 0, 0); if (VMSok (status)) status = IOsb.Status; if (VMSnok (status)) return (status); if (Debug) fprintf (stdout, "%d %d\n", MaxBlock, FreeBlocks); TotalBlocks += MaxBlock; TotalFreeBlocks += FreeBlocks; ErrorCount += ErrCnt; } TotalUsedBlocks = TotalBlocks - TotalFreeBlocks; if (Debug) fprintf (stdout, "%d %d %d %dMB %dMB %dMB %d%% %d%%\n", TotalBlocks, TotalFreeBlocks, TotalUsedBlocks, TotalBlocks >> 11, TotalFreeBlocks >> 11, TotalUsedBlocks >> 11, PercentOf(TotalFreeBlocks,TotalBlocks), PercentOf(TotalUsedBlocks,TotalBlocks)); if (DevNamePtrPtr) *DevNamePtrPtr = CacheDevName; if (TotalBlocksPtr) *TotalBlocksPtr = TotalBlocks; if (UsedBlocksPtr) *UsedBlocksPtr = TotalUsedBlocks; if (FreeBlocksPtr) *FreeBlocksPtr = TotalFreeBlocks; if (ErrorCountPtr) *ErrorCountPtr = ErrorCount; return (SS$_NORMAL); } /*****************************************************************************/ /* Add the information about the latest request to the screen buffer from the request logical. Each plain-text string in this logical is terminated by a null character. */ int AddRequest () { static int PathAlertBellCount = 0, PrevPathAlertCount = 0; static char *LookupHostNamePtr; int status, BytesLength, StringLength; #ifndef __VAX double dValue; #endif char *bptr, *bpsptr, *cptr, *sptr, *tptr; char BytesRawRx [32], BytesRawTx [32], BytesTxGzipPercent [8], RequestDuration [32]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "AddRequest()\n"); CommaNumber (64, &HttpdGblSecPtr->Request.BytesRawRx, BytesRawRx); CommaNumber (64, &HttpdGblSecPtr->Request.BytesRawTx, BytesRawTx); if (HttpdGblSecPtr->Request.BytesTxGzipPercent) sprintf (BytesTxGzipPercent, " (%d%%)", HttpdGblSecPtr->Request.BytesTxGzipPercent); else BytesTxGzipPercent[0] = '\0'; #ifndef __VAX dValue = atof(HttpdGblSecPtr->Request.Duration); if (dValue >= 0.0006) sprintf (RequestDuration, "%.3f", dValue); else if (dValue >= 0.00006) sprintf (RequestDuration, "%.4f", dValue); else #endif strcpy (RequestDuration, HttpdGblSecPtr->Request.Duration); sptr = ScrPtr; sptr += sprintf (sptr, "%s\r\n %sTime:%s %s %sStatus:%s %03d \ %sRx:%s %s %sTx:%s %s%s %sDur:%s %s", ANSIceol, ANSIbold, ANSInormal, HttpdGblSecPtr->Request.Time, ANSIbold, ANSInormal, HttpdGblSecPtr->Request.HttpStatus, ANSIbold, ANSInormal, BytesRawRx, ANSIbold, ANSInormal, BytesRawTx, BytesTxGzipPercent, ANSIbold, ANSInormal, RequestDuration); bpsptr = BytesPerString (HttpdGblSecPtr->Request.BytesPerSecond); BytesLength = strlen(BytesRawRx) + strlen(BytesRawTx) + strlen(BytesTxGzipPercent); if (BytesLength <= 10) sptr += sprintf (sptr, " (%s)", bpsptr); sptr += sprintf (sptr, "%s\r\n\ %sService:%s %s%s%s%s", ANSIceol, ANSIbold, ANSInormal, HttpdGblSecPtr->Request.Service, HttpdGblSecPtr->Request.PrcNam[0] ? " (" : "", HttpdGblSecPtr->Request.PrcNam, HttpdGblSecPtr->Request.PrcNam[0] ? ")" : ""); if (BytesLength >= 12) { StringLength = 50 + BytesLength; StringLength -= 11; StringLength -= strlen(HttpdGblSecPtr->Request.Service); if (HttpdGblSecPtr->Request.PrcNam[0]) StringLength -= strlen(HttpdGblSecPtr->Request.PrcNam) + 3; if (StringLength < 2) StringLength = 2; while (StringLength--) *sptr++ = ' '; sptr += sprintf (sptr, "(%s)", bpsptr); } sptr += sprintf (sptr, "%s\r\n\ %sHost:%s ", ANSIceol, ANSIbold, ANSInormal); cptr = HttpdGblSecPtr->Request.ClientHostName; if (PrevHostName[0]) { if (strcmp (cptr, PrevHostName)) { strcpy (PrevHostName, cptr); LookupHostNamePtr = NULL; if (DoAlertHost) fputs ("\x07", stdout); } } else strcpy (PrevHostName, cptr); if (DoAlertPath && HttpdGblSecPtr->Request.Alert) { if (AccountingPtr->PathAlertCount > PrevPathAlertCount) { PrevPathAlertCount = AccountingPtr->PathAlertCount; PathAlertBellCount = PathAlertBellRepetition; } } if (PathAlertBellCount) { fputs ("\x07", stdout); PathAlertBellCount--; } if (DoLookupHost) { if (!LookupHostNamePtr) { /* check for IPv4 address */ for (tptr = cptr; isdigit(*tptr) || *tptr == '.'; tptr++); if (*tptr) { /* nope, check for IPv6 address */ for (tptr = cptr; isxdigit(*tptr) || *tptr == ':' || *tptr == '-' || *tptr == '.'; tptr++); } if (*tptr) LookupHostNamePtr = NULL; else LookupHostNamePtr = TcpIpLookupHostName (cptr, 0, NULL); } if ((tptr = LookupHostNamePtr) && *tptr) { /* looks like a resolveable IPv4 or IPv6 address */ tptr = LookupHostNamePtr; StringLength = strlen(cptr) + strlen(tptr); /* excise some middle section if it would overflow the line */ if (StringLength > 54) { while (*tptr && *tptr != '.') *sptr++ = *tptr++; *sptr++ = '.'; *sptr++ = '.'; while (*tptr && --StringLength > 54) tptr++; while (*tptr && *tptr != '.') tptr++; } while (*tptr) *sptr++ = *tptr++; *sptr++ = ' '; *sptr++ = '('; while (*cptr) *sptr++ = *cptr++; *sptr++ = ')'; } else while (*cptr) *sptr++ = *cptr++; } else while (*cptr) *sptr++ = *cptr++; switch (HttpdGblSecPtr->Request.HttpProtocol) { case HTTP_VERSION_2 : cptr = " HTTP/2"; break; case HTTP_VERSION_1_1 : cptr = " HTTP/1.1"; break; case HTTP_VERSION_1_0 : cptr = " HTTP/1.0"; break; case HTTP_VERSION_0_9 : cptr = " HTTP/0.9"; break; default : cptr = ""; } while (*cptr) *sptr++ = *cptr++; if (HttpdGblSecPtr->Request.ReadError[0]) { sptr += sprintf (sptr, "%s\r\n %sRx-ERR:%s ", ANSIceol, ANSIbold, ANSInormal); tptr = sptr + 68; for (cptr = HttpdGblSecPtr->Request.ReadError; *cptr && sptr < tptr; *sptr++ = *cptr++); } if (HttpdGblSecPtr->Request.WriteError[0]) { sptr += sprintf (sptr, "%s\r\n %sTx-ERR:%s ", ANSIceol, ANSIbold, ANSInormal); tptr = sptr + 68; for (cptr = HttpdGblSecPtr->Request.WriteError; *cptr && sptr < tptr; *sptr++ = *cptr++); } sptr += sprintf (sptr, "%s\r\n %sRequest:%s %s", ANSIceol, ANSIbold, ANSInormal, HttpdGblSecPtr->Request.Alert ? ANSIbold : ""); /* limit the request string to 3 x 80 character lines minus field name */ tptr = sptr + 226; /* allow for foregoing error reports */ if (HttpdGblSecPtr->Request.ReadError[0]) tptr -= 80; if (HttpdGblSecPtr->Request.WriteError[0]) tptr -= 80; if (HttpdGblSecPtr->Request.AuthUser[0]) tptr -= strlen(HttpdGblSecPtr->Request.AuthUser) + 3; if (tptr < sptr) tptr = sptr; for (cptr = HttpdGblSecPtr->Request.MethodName; *cptr && sptr < tptr; *sptr++ = *cptr++); if (HttpdGblSecPtr->Request.Uri[0] && sptr < tptr) *sptr++ = ' '; for (cptr = HttpdGblSecPtr->Request.Uri; *cptr && sptr < tptr; cptr++) if ((*cptr >= 0x20 && *cptr <= 0x7e) || (*cptr >= 0xa0 && *cptr <= 0xfe)) *sptr++ = *cptr; else *sptr++ = '.'; if (sptr >= tptr) sptr += sprintf (sptr, "%s...%s", ANSIbold, ANSInormal); else if (HttpdGblSecPtr->Request.Alert) sptr += sprintf (sptr, "%s", ANSInormal); if (HttpdGblSecPtr->Request.AuthUser[0]) sptr += sprintf (sptr, " (%s)", HttpdGblSecPtr->Request.AuthUser); sptr += sprintf (sptr, "%s", ANSIceol); ScrPtr = sptr; return (SS$_NORMAL); } /*****************************************************************************/ /* */ int AddStatusMessage () { /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "AddStatusMessage()\n"); ScrPtr += sprintf (ScrPtr, "%s\r\n %sSTATUS:%s %s%s", ANSIceos, ANSIbold, ANSInormal, HttpdGblSecPtr->StatusMessage, ANSIceol); return (SS$_NORMAL); } /*****************************************************************************/ /* Calculate percentages of unsigned longs using floats to avoid integer overflow and allowing more accurate rounding. */ int PercentOf ( unsigned long arg1, unsigned long arg2 ) { int iperc; float farg1, farg2, fperc; /*********/ /* begin */ /*********/ if (arg2) { farg1 = (float)arg1; farg2 = (float)arg2; fperc = farg1 * 100.0 / farg2; iperc = (int)fperc; if (fperc - (float)iperc >= 0.5) iperc++; return (iperc); } return (0); } /*****************************************************************************/ /* Calculate percentages of quadwards using floats to avoid overflow and allowing more accurate rounding. */ int QuadPercentOf ( unsigned long *qarg1, unsigned long *qarg2 ) { int iqperc; float fqarg1, fqarg2, fqperc; /*********/ /* begin */ /*********/ if (qarg2[0] || qarg2[1]) { fqarg1 = (float)qarg1[0] + ((float)qarg1[1] * 4294967296.0); fqarg2 = (float)qarg2[0] + ((float)qarg2[1] * 4294967296.0); if (fqarg2 == 0.0) return (0); fqperc = fqarg1 * 100.0 / fqarg2; iqperc = (int)fqperc; if (fqperc - (float)iqperc >= 0.5) iqperc++; return (iqperc); } return (0); } /*****************************************************************************/ /* Get the host name using synchronous address-to-name IPv4/IPv6 lookup. Supply either an IP address string, or an IPv4 or IPv6 address. The IP address string must be terminated by white-space or a null. The static host name buffer should not be modified by the calling routine. (This code is *derived* from a similar functionality in [SRC.HTTPD]TCPIP.C) */ char* TcpIpLookupHostName ( char *IpAddressString, int Ip4Address, int *Ip6AddressPtr ) { /* this is one second delta */ static unsigned long RetryDelta [QUAD2] = { -10000000, -1 }; static unsigned char ControlSubFunction [4] = { INETACP_FUNC$C_GETHOSTBYADDR, INETACP$C_TRANS, 0, 0 }; static struct dsc$descriptor ControlSubFunctionDsc = { 4, DSC$K_DTYPE_T, DSC$K_CLASS_S, (char*)&ControlSubFunction }; static unsigned short LookupChannel; static char HostName [127+1]; static $DESCRIPTOR (HostNameDsc, HostName); int status, RetryAttempts; unsigned short HostNameLength; int LocalIp6Address [4]; char *cptr, *sptr, *zptr; struct dsc$descriptor *dscptr; struct dsc$descriptor HostAddressDsc; IO_SB LookupIOsb; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "TcpIpLookupHostName()\n"); /* assign a channel to the internet template device */ if (!LookupChannel) { status = sys$assign (&TcpIpDeviceDsc, &LookupChannel, 0, 0); if (VMSnok (status)) exit (status); } if (!Ip6AddressPtr) Ip6AddressPtr = LocalIp6Address; if (IpAddressString) { Ip4Address = 0; memset (Ip6AddressPtr, 0, 16); status = TcpIpStringAddress (IpAddressString, &Ip4Address, Ip6AddressPtr); if (VMSnok (status)) return (NULL); } if (Ip4Address) cptr = TcpIpHostCache (NULL, &Ip4Address, NULL); else cptr = TcpIpHostCache (NULL, NULL, Ip6AddressPtr); if (cptr) if (*cptr == '?') return (NULL); else return (cptr); dscptr = (struct dsc$descriptor*)&HostNameDsc; dscptr->dsc$b_class = DSC$K_CLASS_S; dscptr->dsc$b_dtype = DSC$K_DTYPE_T; dscptr->dsc$w_length = sizeof(HostName)-1; dscptr->dsc$a_pointer = HostName; dscptr = &HostAddressDsc; dscptr->dsc$b_class = DSC$K_CLASS_S; dscptr->dsc$b_dtype = DSC$K_DTYPE_T; if (Ip4Address) { dscptr->dsc$w_length = 4; dscptr->dsc$a_pointer = (char*)&Ip4Address; } else if (Ip6AddressPtr) { dscptr->dsc$w_length = 16; dscptr->dsc$a_pointer = (char*)Ip6AddressPtr; } else exit (SS$_BUGCHECK); #ifdef TCPIP_LOOKUP_HOST_NAME_RETRY RetryAttempts = TCPIP_LOOKUP_HOST_NAME_RETRY; #else RetryAttempts = 10; #endif while (RetryAttempts--) { status = sys$qiow (0, LookupChannel, IO$_ACPCONTROL, &LookupIOsb, 0, 0, &ControlSubFunctionDsc, &HostAddressDsc, &HostNameLength, &HostNameDsc, 0, 0); if (Debug) fprintf (stdout, "sys$qiow() %%X%08.08X %%X%08.08X\n", status, LookupIOsb.Status); if (VMSnok (status)) LookupIOsb.Status = status; if (VMSok (LookupIOsb.Status)) break; sys$schdwk (0, 0, &RetryDelta, 0); sys$hiber(); } if (VMSok (LookupIOsb.Status)) { HostName[HostNameLength] = '\0'; if (Debug) fprintf (stdout, "|%s|\n", HostName); /* set the cache entry */ if (Ip4Address) TcpIpHostCache (HostName, &Ip4Address, NULL); else TcpIpHostCache (HostName, NULL, Ip6AddressPtr); return (HostName); } else { /* a cache entry indicating it couldn't be resolved */ if (Ip4Address) TcpIpHostCache ("?", &Ip4Address, NULL); else TcpIpHostCache ("?", NULL, Ip6AddressPtr); return (NULL); } } /*****************************************************************************/ /* Get the IP address using synchronous name-to-address Ipv4/Ipv6 lookup. The host name string must be terminated by white-space or a null. (This code is *derived* from a similar functionality in [SRC.HTTPD]TCPIP.C) */ char* TcpIpLookupAddress ( char *HostName, int *Ip4AddressPtr, int *Ip6AddressPtr ) { /* this is one second delta */ static unsigned long RetryDelta [QUAD2] = { -10000000, -1 }; static unsigned char ControlSubFunction [4] = { INETACP_FUNC$C_GETHOSTBYNAME, INETACP$C_TRANS, 0, 0 }; static struct dsc$descriptor AddressDsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; static struct dsc$descriptor ControlSubFunctionDsc = { 4, DSC$K_DTYPE_T, DSC$K_CLASS_S, (char*)&ControlSubFunction }; static unsigned short LookupChannel; int status, RetryAttempts; unsigned short AddressLength, HostNameLength; char *cptr, *sptr, *zptr; int Bytes16 [4]; struct dsc$descriptor *dscptr; struct dsc$descriptor HostAddressDsc; struct dsc$descriptor HostNameDsc; IO_SB LookupIOsb; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "TcpIpLookupAddress()\n"); /* assign a channel to the internet template device */ if (!LookupChannel) { status = sys$assign (&TcpIpDeviceDsc, &LookupChannel, 0, 0); if (VMSnok (status)) exit (status); } for (cptr = HostName; *cptr; cptr++); HostNameLength = cptr - HostName; /* first, IPv4 literal address */ for (cptr = HostName; isdigit(*cptr) || *cptr == '.'; cptr++); if (*cptr) { /* if not then, IPv6 literal address */ for (cptr = HostName; isxdigit(*cptr) || *cptr == ':' || *cptr == '-'; cptr++); } if (!*cptr) { /*******************/ /* literal address */ /*******************/ if (Ip4AddressPtr) *Ip4AddressPtr = 0; if (Ip6AddressPtr) memset (Ip6AddressPtr, 0, 16); status = TcpIpStringAddress (HostName, Ip4AddressPtr, Ip6AddressPtr); if (VMSok (status)) return (HostName); else return (NULL); } cptr = TcpIpHostCache (HostName, Ip4AddressPtr, Ip6AddressPtr); if (cptr) if (*cptr == '?') return (NULL); else return (cptr); dscptr = (struct dsc$descriptor*)&HostNameDsc; dscptr->dsc$b_class = DSC$K_CLASS_S; dscptr->dsc$b_dtype = DSC$K_DTYPE_T; dscptr->dsc$w_length = HostNameLength; dscptr->dsc$a_pointer = HostName; dscptr = &HostAddressDsc; dscptr->dsc$b_class = DSC$K_CLASS_S; dscptr->dsc$b_dtype = DSC$K_DTYPE_T; /* give the full buffer and then check the returned length */ dscptr->dsc$w_length = sizeof(Bytes16); dscptr->dsc$a_pointer = (char*)Bytes16; memset (&Bytes16, 0, sizeof(Bytes16)); #ifdef TCPIP_LOOKUP_HOST_NAME_RETRY RetryAttempts = TCPIP_LOOKUP_HOST_NAME_RETRY; #else RetryAttempts = 10; #endif while (RetryAttempts--) { status = sys$qiow (0, LookupChannel, IO$_ACPCONTROL, &LookupIOsb, 0, 0, &ControlSubFunctionDsc, &HostNameDsc, &AddressLength, &HostAddressDsc, 0, 0); if (Debug) fprintf (stdout, "sys$qiow() %%X%08.08X %%X%08.08X\n", status, LookupIOsb.Status); if (VMSnok (status)) LookupIOsb.Status = status; if (VMSok (LookupIOsb.Status)) break; sys$schdwk (0, 0, &RetryDelta, 0); sys$hiber(); } if (VMSok(LookupIOsb.Status)) { if (AddressLength != 4 && AddressLength != 16) exit (SS$_BUGCHECK); /* set an entry in the host cache, copy the address to the pointer */ if (AddressLength == 4) { TcpIpHostCache (HostName, Bytes16, NULL); memcpy (Ip4AddressPtr, Bytes16, 4); } else { TcpIpHostCache (HostName, NULL, Bytes16); memcpy (Ip6AddressPtr, Bytes16, 16); } } else { /* a cache entry indicating it couldn't be resolved */ if (AddressLength == 4) TcpIpHostCache ("?", Bytes16, NULL); else TcpIpHostCache ("?", NULL, Bytes16); return (NULL); } } /*****************************************************************************/ /* Maintains and allows lookup of a host name/address Ipv4/Ipv6 cache. To lookup name->address supply a non-NULL 'HostName' and pointer(s) to 'Ip4AddressPtr' (an int) and 'Ip6AddressPtr' (an array of int). To lookup address->name supply one of 'Ip4AddressPtr' or 'Ip6AddressPtr' and a NULL 'HostName'. A pointer to host name is returned, or NULL. To set an entry, supply 'HostName' and one of 'Ip4AddressPtr' (an int) and 'Ip6AddressPtr' (an array of int). Supplying a zero address cancels the entry. The host name must be delimitted by white-space or a null. (This code is *derived* from a similar functionality in [SRC.HTTPD]TCPIP.C) */ char* TcpIpHostCache ( char *HostName, int *Ip4AddressPtr, int *Ip6AddressPtr ) { #define HOST_CACHE_CHUNK 64 #define HOST_CACHE_EXPIRE_SECONDS 600 typedef struct _HOST_CACHE_ENTRY { char HostName [127+1]; int Ip4Address; int Ip6Address [4]; int ExpireSecond, HostNameLength; } HOST_CACHE_ENTRY; static int Ip6AddressZero [4]; static int HostCacheCount, HostCacheMax; static HOST_CACHE_ENTRY *HostCachePtr; int cnt, HostNameLength; unsigned long CurrentSecond; char *cptr, *sptr, *zptr; HOST_CACHE_ENTRY *hcptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "TcpIpHostCache() %d %d %d %d %d\n", HostCacheMax, HostCacheCount, HostName, Ip4AddressPtr, Ip6AddressPtr); if (!HostCachePtr) { HostCacheMax = HOST_CACHE_CHUNK; HostCachePtr = calloc (HostCacheMax, sizeof(HOST_CACHE_ENTRY)); } time (&CurrentSecond); if (HostName && Ip4AddressPtr && Ip6AddressPtr) { /*******************/ /* name to address */ /*******************/ for (cptr = HostName; *cptr; cptr++); HostNameLength = cptr - HostName; hcptr = HostCachePtr; for (cnt = 0; cnt < HostCacheCount; hcptr++, cnt++) { if (hcptr->HostNameLength != HostNameLength) continue; if (memcmp (hcptr->HostName, HostName, HostNameLength)) continue; if (hcptr->ExpireSecond > CurrentSecond) { /* not-expired hit! */ if (Ip4AddressPtr) *Ip4AddressPtr = hcptr->Ip4Address; if (Ip6AddressPtr) memcpy (Ip6AddressPtr, hcptr->Ip6Address, 16); if (Debug) fprintf (stdout, "name->addr HIT\n"); return (HostName); } /* expired */ hcptr->HostNameLength = 0; if (Debug) fprintf (stdout, "name->addr EXPIRED\n"); return (NULL); } if (Debug) fprintf (stdout, "name->addr MISS\n"); return (NULL); } if (!HostName && (Ip4AddressPtr || Ip6AddressPtr)) { /*******************/ /* address to name */ /*******************/ hcptr = HostCachePtr; for (cnt = 0; cnt < HostCacheCount; hcptr++, cnt++) { if (!hcptr->HostNameLength) continue; if (Ip4AddressPtr && *Ip4AddressPtr != hcptr->Ip4Address) continue; if (Ip6AddressPtr && memcmp (Ip6AddressPtr, hcptr->Ip6Address, 16)) continue; if (hcptr->ExpireSecond > CurrentSecond) { /* not-expired hit! */ if (Debug) fprintf (stdout, "addr->name HIT\n"); return (hcptr->HostName); } /* expired */ hcptr->HostNameLength = 0; if (Debug) fprintf (stdout, "addr->name EXPIRED\n"); return (NULL); } if (Debug) fprintf (stdout, "addr->name MISS\n"); return (NULL); } if ((HostName && Ip4AddressPtr && !Ip6AddressPtr) || (HostName && !Ip4AddressPtr && Ip6AddressPtr)) { /*******************/ /* set cache entry */ /*******************/ for (cptr = HostName; *cptr; cptr++); HostNameLength = cptr - HostName; /* check if there an equivalent entry already in the cache */ hcptr = HostCachePtr; for (cnt = 0; cnt < HostCacheCount; hcptr++, cnt++) { if (hcptr->HostNameLength != HostNameLength) continue; if (memcmp (hcptr->HostName, HostName, HostNameLength)) continue; /* yes, already in the cache */ if (Ip4AddressPtr && !*Ip4AddressPtr || Ip6AddressPtr && memcmp (Ip6AddressPtr, Ip6AddressZero, 16)) { /* cancel the entry */ hcptr->HostNameLength = 0; if (Debug) fprintf (stdout, "RESET\n"); return (NULL); } if (Debug) fprintf (stdout, "set ALREADY\n"); return (NULL); } /* check for an entry available for reuse */ hcptr = HostCachePtr; for (cnt = 0; cnt < HostCacheCount; hcptr++, cnt++) { if (!hcptr->HostNameLength) break; if (hcptr->ExpireSecond < CurrentSecond) { /* this one needs refreshing */ hcptr->HostNameLength = 0; break; } } if (cnt >= HostCacheCount) { /* nothing available for reuse */ if (HostCacheCount < HostCacheMax) { /* use the next entry */ HostCacheCount++; if (Debug) fprintf (stdout, "set NEXT\n"); } else { /* out of next entries, expand the cache */ HostCacheMax += HOST_CACHE_CHUNK; HostCachePtr = calloc (HostCacheMax, sizeof(HOST_CACHE_ENTRY)); hcptr = &HostCachePtr[HostCacheCount++]; if (Debug) fprintf (stdout, "set EXPAND\n"); } } /* populate the entry */ zptr = (sptr = hcptr->HostName) + sizeof(hcptr->HostName)-1; for (cptr = HostName; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; hcptr->HostNameLength = sptr - hcptr->HostName; if (Ip4AddressPtr) hcptr->Ip4Address = *Ip4AddressPtr; else hcptr->Ip4Address = 0; if (Ip6AddressPtr) memcpy (hcptr->Ip6Address, Ip6AddressPtr, 16); else memset (hcptr->Ip6Address, 0, 16); hcptr->ExpireSecond = CurrentSecond + HOST_CACHE_EXPIRE_SECONDS; if (Debug) fprintf (stdout, "SET\n"); return (HostName); } exit (SS$_BUGCHECK); } /*****************************************************************************/ /* Convert an IPv4 dotted-decimal or IPv6 hexadecimal format (normal or compressed) string into an appropriate address. The address must be delimited by white-space or a null. The white-space criterion is important in any modification from TCPIP.C! (This code is *derived* from a similar functionality in [SRC.HTTPD]TCPIP.C) */ int TcpIpStringAddress ( char *String, int *Ip4AddressPtr, int *Ip6AddressPtr ) { int cnt, idx, Ip4Address; int Ip4Octets [4]; unsigned short Ip6Address [8]; unsigned short *ip6ptr; unsigned int uint; unsigned int Ip6Octets [10]; /* well sort-of */ char *cptr, *sptr, *zptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "TcpIpStringAddress() |%s|\n", String); if (Ip4AddressPtr) *Ip4AddressPtr = 0; if (Ip6AddressPtr) memset (Ip6AddressPtr, 0, 16); /* ::FFFF:n.n.n.n or --FFFF-n.n.n.n */ if (!strncmp (sptr = String, "::FFFF:", 7) || !strncmp (sptr = String, "--FFFF-", 7)) sptr += 7; /* will reach end-of-string if it's an IPv4 address */ for (cptr = sptr; isdigit(*cptr) || *cptr == '.'; cptr++); if (!*cptr) { /********/ /* IPv4 */ /********/ memset (Ip4Octets, 0, sizeof(Ip4Octets)); cnt = sscanf (sptr, "%d.%d.%d.%d", &Ip4Octets[0], &Ip4Octets[1], &Ip4Octets[2], &Ip4Octets[3]); if (cnt != 4) return (SS$_ENDOFFILE); Ip4Address = 0; for (idx = 0; idx <= 3; idx++) { if (Ip4Octets[idx] < 0 || Ip4Octets[idx] > 255) return (SS$_ENDOFFILE); Ip4Address |= Ip4Octets[idx] << idx * 8; } if (Debug) fprintf (stdout, "IPv4 %08.08X\n", Ip4Address); *Ip4AddressPtr = Ip4Address; return (SS$_NORMAL); } /********/ /* IPv6 */ /********/ memset (Ip4Octets, 0, sizeof(Ip4Octets)); memset (Ip6Octets, 0, sizeof(Ip6Octets)); /* _normal_ _compressed_ 1070:0:0:0:0:800:200C:417B 1070::800:200C:417B 0:0:0:0:0:0:13.1.68.3 ::13.1.68.3 0:0:0:0:0:FFFF:129.144.52.38 ::FFFF:129.144.52.38 _hyphen-variant_ 1070-0-0-0-0-800-200C-417B 1070--800-200C-417B 0-0-0-0-0-0-13.1.68.3 --13.1.68.3 0-0-0-0-0-FFFF-129.144.52.38 --FFFF-129.144.52.38 */ idx = 0; zptr = ""; cptr = String; while (*cptr) { if (idx > 7) return (SS$_ENDOFFILE); /* look ahead at the next delimiter */ for (sptr = cptr; isxdigit(*sptr); sptr++); if (*sptr == ':' || (!*sptr && *zptr == ':') || *sptr == '-' || (!*sptr && *zptr == '-')) { /* IPv6 (or variant) syntax */ uint = (unsigned long)strtol (cptr, NULL, 16); if (uint > 0xffff) return (SS$_ENDOFFILE); /* network byte-order */ Ip6Octets[idx] = (uint >> 8) | (uint << 8); idx++; if (*(unsigned short*)sptr == '::' || *(unsigned short*)sptr == '--') { /* indicate the ellipsis zeroes */ Ip6Octets[idx] = 0xffffffff; idx++; sptr++; } } else if (*sptr == '.' || (!*sptr && *zptr == '.')) { /* dropped into dotted-decimal, IPv4 compatible syntax */ cnt = sscanf (cptr, "%d.%d.%d.%d", &Ip4Octets[3], &Ip4Octets[2], &Ip4Octets[1], &Ip4Octets[0]); if (cnt != 4) return (SS$_ENDOFFILE); while (isdigit(*cptr) || *cptr == '.') cptr++; if (*cptr) return (SS$_ENDOFFILE); if (Ip4Octets[0] < 0 || Ip4Octets[0] > 255) return (SS$_ENDOFFILE); if (Ip4Octets[1] < 0 || Ip4Octets[1] > 255) return (SS$_ENDOFFILE); if (Ip4Octets[2] < 0 || Ip4Octets[2] > 255) return (SS$_ENDOFFILE); if (Ip4Octets[3] < 0 || Ip4Octets[3] > 255) return (SS$_ENDOFFILE); Ip6Octets[idx++] = (Ip4Octets[3] << 8) | Ip4Octets[2]; Ip6Octets[idx++] = (Ip4Octets[1] << 8) | Ip4Octets[0]; break; } else return (SS$_ENDOFFILE); cptr = zptr = sptr; if (*cptr) cptr++; } memset (ip6ptr = Ip6Address, 0, sizeof(Ip6Address)); cnt = 9 - idx; for (idx = 0; idx < 8; idx++) { if (Ip6Octets[idx] == 0xffffffff) { if (cnt < 0) return (SS$_ENDOFFILE); while (cnt--) ip6ptr++; } else *ip6ptr++ = Ip6Octets[idx]; } if (Debug) fprintf (stdout, "IPv6 %04.04X%04.04X%04.04X%04.04X%04.04X%04.04X%04.04X%04.04X\n", Ip6Address[7], Ip6Address[6], Ip6Address[5], Ip6Address[4], Ip6Address[3], Ip6Address[2], Ip6Address[1], Ip6Address[0]); memcpy (Ip6AddressPtr, Ip6Address, 16); return (SS$_NORMAL); } /*****************************************************************************/ /* 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! If the number of servers varies then (by default) sound the bell!! */ GetInstanceInformation () { static int PrevClusterCount = -1, PrevNodeCount = -1; static char ClusterLockName [32], NodeLockName [32], PrevInstanceString [32]; static $DESCRIPTOR (ClusterLockNameDsc, ClusterLockName); static $DESCRIPTOR (NodeLockNameDsc, NodeLockName); static LKIDEF LkiLocks [32]; static struct lksb ClusterLockLksb, NodeLockLksb; static struct { unsigned short TotalLength, /* bits 0..15 */ LockLength; /* bits 16..30 */ } ReturnLength; static struct { unsigned short buf_len; unsigned short item; unsigned char *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; IO_SB IOsb; LKIDEF *lkiptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "GetInstanceInformation()\n"); status = sys$setprv (1, &SysLckMask, 0, 0); if (status == SS$_NOTALLPRIV) exit (SS$_NOPRIV); if (!ClusterLockName[0]) { if (InstanceEnvNumber > INSTANCE_ENV_NUMBER_MAX) { fprintf (stdout, "%%%s-E-INSTANCE, group number range 1 to %d\n", Utility, INSTANCE_ENV_NUMBER_MAX); exit (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; if (Debug) fprintf (stdout, "cluster: |%*.*s|\n", ClusterLockNameDsc.dsc$w_length, ClusterLockNameDsc.dsc$w_length, ClusterLockNameDsc.dsc$a_pointer); /* 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 (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 = SyiNodeName; *cptr && sptr < zptr; *sptr++ = *cptr++); if (sptr < zptr) *sptr++ = (char)INSTANCE_NODE; NodeLockNameDsc.dsc$w_length = sptr - NodeLockName; if (Debug) fprintf (stdout, "node: |%*.*s|\n", NodeLockNameDsc.dsc$w_length, NodeLockNameDsc.dsc$w_length, NodeLockNameDsc.dsc$a_pointer); /* 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 (status); } /* get and count the cluster instance locks */ status = sys$getlkiw (0, &ClusterLockLksb.lksb$l_lkid, &LkiItems, &IOsb, 0, 0, 0); if (VMSok (status)) status = ClusterLockLksb.lksb$w_status; if (VMSnok (status)) exit (status); if (ReturnLength.LockLength) { /* if insufficient buffer space */ if (ReturnLength.LockLength & 0x8000) exit (SS$_BADPARAM); LockCount = ReturnLength.TotalLength / ReturnLength.LockLength; } else LockCount = 0; if (Debug) fprintf (stdout, "cluster LockCount: %d\n", LockCount); ClusterCount = 0; lkiptr = &LkiLocks; for (cnt = LockCount; cnt; cnt--) { if (Debug) fprintf (stdout, "1 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 (0, &NodeLockLksb.lksb$l_lkid, &LkiItems, &IOsb, 0, 0, 0); if (VMSok (status)) status = NodeLockLksb.lksb$w_status; if (VMSnok (status)) exit (status); if (ReturnLength.LockLength) { /* if insufficient buffer space */ if (ReturnLength.LockLength & 0x8000) exit (SS$_BADPARAM); LockCount = ReturnLength.TotalLength / ReturnLength.LockLength; } else LockCount = 0; if (Debug) fprintf (stdout, "node LockCount: %d\n", LockCount); NodeCount = 0; lkiptr = &LkiLocks; for (cnt = LockCount; cnt; cnt--) { if (Debug) fprintf (stdout, "2 lki$b_grmode: %d\n", lkiptr->lki$b_grmode); if (lkiptr->lki$b_grmode != LCK$K_NLMODE) NodeCount++; lkiptr++; } if (++InstanceCount > NodeCount) InstanceCount = 1; NextInstancePid = NonNlLockCount = 0; lkiptr = &LkiLocks; for (cnt = LockCount; cnt; cnt--) { if (Debug) fprintf (stdout, "3 lki$b_grmode: %d\n", lkiptr->lki$b_grmode); if (lkiptr->lki$b_grmode != LCK$K_NLMODE) { NonNlLockCount++; if (Debug) fprintf (stdout, "%d %d\n", InstanceCount, NonNlLockCount); if (InstanceCount == NonNlLockCount) { if (Debug) fprintf (stdout, "lki$l_pid: %08x\n", lkiptr->lki$l_pid); NextInstancePid = lkiptr->lki$l_pid; break; } } lkiptr++; } sys$setprv (0, &SysLckMask, 0, 0); /* build the string */ if (PrevClusterCount < 0) { /* initialize */ PrevClusterCount = ClusterCount; PrevNodeCount = NodeCount; } if (NodeCount != PrevNodeCount || ClusterCount != PrevClusterCount) { sptr = PrevInstanceString; *sptr++ = '('; for (cptr = InstanceString + sizeof(ANSIbold)-1; isdigit(*cptr) || *cptr == '/'; *sptr++ = *cptr++); *sptr++ = ')'; *sptr = '\0'; } sptr = InstanceString; sprintf (sptr, "%s%d", ANSIbold, NodeCount); while (*sptr) sptr++; if (SyiClusterNodes > 1) { sprintf (sptr, "/%d", ClusterCount); while (*sptr) sptr++; } strcpy (sptr, ANSInormal); while (*sptr) sptr++; if (PrevInstanceString[0]) { for (cptr = PrevInstanceString; *cptr; *sptr++ = *cptr++); *sptr = '\0'; } /* determine the length before adding the non-displaying bells!! */ InstanceStringLength = sptr - InstanceString; InstanceStringLength -= sizeof(ANSIbold)-1 + sizeof(ANSInormal)-1; if (NodeCount != PrevNodeCount || ClusterCount != PrevClusterCount) { PrevClusterCount = ClusterCount; PrevNodeCount = NodeCount; for (cptr = "\x07\x07\x07"; *cptr; *sptr++ = *cptr++); *sptr = '\0'; } if (Debug) fprintf (stdout, "InstanceString: |%s|\n", InstanceString); } /*****************************************************************************/ /* See [SRC.HTTPD]INSTANCE.C InstanceCliReport(). */ void AddInstanceStatus () { static ulong LibDeltaMins = LIB$K_DELTA_MINUTES; BOOL stale; int idx, len, maxtab, maxlen, status, total; ulong MinsAgo; ulong AgoTime64 [2]; char *cptr; char number [64], ExitAgoBuf [16], ExitStatusBuf [16], LineBuffer [256], StartAgoBuf [16], TmpAgoBuf [16], UpdateAgoBuf [16]; INSTANCE_STATUS *istptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "AddInstanceStatus() %d\n", PageLength); maxtab = AccountingPtr->InstanceStatusTableCount; if (maxtab == 1 && !DoStatusInfo) return; if (!DoStatusInfo) { if (PageLength <= 24) return; if (PageLength < 24 + maxtab + 2) return; } for (idx = maxlen = 0; idx < maxtab; idx++) { istptr = &AccountingPtr->InstanceStatusTable[idx]; if ((len = strlen(istptr->InstanceName)) > maxlen) maxlen = len; } if (maxlen < 8) maxlen = 8; ScrPtr += sprintf (ScrPtr, "\r\n%s\r\n\ %s%-*s%s %s%4.4s%s %s%4.4s%s %s%5.5s%s %s%4.4s%s \ %s%-10s%s %s%7.7s%s %s%4.4s%s %s%6.6s%s\r\n", ANSIceos, ANSIbold, maxlen, "Instance", ANSInormal, ANSIbold, " Ago", ANSInormal, ANSIbold, " Up", ANSInormal, ANSIbold, "Count", ANSInormal, ANSIbold, "Exit", ANSInormal, ANSIbold, "Status", ANSInormal, ANSIbold, "Version", ANSInormal, ANSIbold, "/Min", ANSInormal, ANSIbold, " /Hour", ANSInormal); for (idx = total = 0; idx < maxtab; idx++) { istptr = &AccountingPtr->InstanceStatusTable[idx]; /* if a purged entry */ if (QUAD_ZERO(istptr->UpdateTime64)) continue; /* right justify each of these */ ThisLongAgo (&istptr->ExitTime64, TmpAgoBuf); sprintf (ExitAgoBuf, "%4s", TmpAgoBuf); ThisLongAgo (&istptr->StartTime64, TmpAgoBuf); sprintf (StartAgoBuf, "%4s", TmpAgoBuf); ThisLongAgo (&istptr->UpdateTime64, TmpAgoBuf); sprintf (UpdateAgoBuf, "%4s", TmpAgoBuf); if (istptr->ExitStatus) sprintf (ExitStatusBuf, "%%X%08.08X", istptr->ExitStatus); else ExitStatusBuf[0] = '\0'; status = lib$sub_times (&NowTime64, &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; strcpy (number, " "); } else { stale = false; sprintf (number, " %s%2d%s ", ANSIbold, ++total, ANSInormal); } for (cptr = number; *cptr; cptr++); sprintf (LineBuffer, "%s%-*s %4.4s %4.4s %5u %4.4s %10.10s %7.7s %4u %6u\r\n", number, maxlen, istptr->InstanceName, UpdateAgoBuf, StartAgoBuf, istptr->StartupCount, ExitAgoBuf, ExitStatusBuf, istptr->HttpdVersion, istptr->MinuteCount, istptr->HourCount); if (stale) for (cptr = LineBuffer + (cptr-number); *cptr; cptr++) if (*cptr == ' ') *cptr = '-'; ScrPtr += sprintf (ScrPtr, "%s", LineBuffer); } if (!total) ScrPtr += sprintf (ScrPtr, " %s 0%s reset?\r\n", ANSIbold, ANSInormal); ScrPtr -= 2; *ScrPtr++ = ' '; *ScrPtr++ = ' '; *ScrPtr++ = '\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. */ int ThisLongAgo ( ulong *Time64Ptr, char *AgoBuf ) { static ulong LibDeltaSecs = LIB$K_DELTA_SECONDS; static $DESCRIPTOR (AgoFaoDsc, "!UL!AZ\0"); static $DESCRIPTOR (AgoBufDsc, ""); int status; ulong SecsAgo; ulong AgoTime64 [2]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ThisLongAgo()\n"); AgoBuf[0] = '\0'; if (QUAD_ZERO(Time64Ptr)) return (SS$_NORMAL); status = lib$sub_times (&NowTime64, Time64Ptr, &AgoTime64); if (VMSok (status)) status = lib$cvt_from_internal_time (&LibDeltaSecs, &SecsAgo, &AgoTime64); if (VMSnok (status)) return (status); 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"); return (status); } /*****************************************************************************/ /* */ int OnControlY (void *FunctionAddress) { static BOOL Disabled = false; static unsigned long Mask = LIB$M_CLI_CTRLY, OldMask; static unsigned short TTChannel = 0; int status; $DESCRIPTOR (TTDsc, "TT:"); /*********/ /* begin */ /*********/ if (FunctionAddress) { if (!TTChannel) if (VMSnok (status = sys$assign (&TTDsc, &TTChannel, 0, 0, 0))) return (status); if (VMSnok (status = sys$qiow (0, TTChannel, IO$_SETMODE | IO$M_CTRLYAST, 0, 0, 0, FunctionAddress, 0, PSL$C_USER, 0, 0, 0))) return (status); if (VMSnok (status = sys$qiow (0, TTChannel, IO$_SETMODE | IO$M_CTRLCAST, 0, 0, 0, FunctionAddress, 0, PSL$C_USER, 0, 0, 0))) return (status); if (!Disabled) { Disabled = true; return (lib$disable_ctrl (&Mask, &OldMask)); } else return (status); } else { sys$cancel (TTChannel); return (lib$enable_ctrl (&OldMask, 0)); } } /*****************************************************************************/ /* */ ControlY_AST () { ControlY = true; sys$wake (0, 0); } /*****************************************************************************/ /* Place the values of selected server statistics into global CLI symbols. This allows a process to periodically pooll these values and do whatever it likes with the provided values. Exits immediately. (A Swoose special :-) */ int PluckStats () { int idx, status; char LastExitStatus [16]; /*********/ /* begin */ /*********/ if (VMSnok (status = MapGlobalSection ())) return (status); AccountingPtr = &HttpdGblSecPtr->Accounting; sprintf (LastExitStatus, "%%X%08.08X", AccountingPtr->LastExitStatus); SetSymbol ("HTTPDMON_VERSION", HttpdGblSecPtr->HttpdVersion, 0); SetSymbol ("HTTPDMON_LASTEXIT", LastExitStatus, 0); SetSymbol ("HTTPDMON_CONNECT_CURRENT", NULL, AccountingPtr->CurrentConnected[HTTP12]); SetSymbol ("HTTPDMON_CONNECT_PEAK", NULL, AccountingPtr->ConnectPeak[HTTP12]); SetSymbol ("HTTPDMON_REQUEST_CURRENT", NULL, AccountingPtr->CurrentProcessing[HTTP12]); SetSymbol ("HTTPDMON_REQUEST_PEAK", NULL, AccountingPtr->ProcessingPeak[HTTP12]); return (SS$_NORMAL); } /****************************************************************************/ /* Assign a global symbol. If the string pointer is null the numeric value is used. Symbol lengths are fixed to a maxim,um of 255. */ SetSymbol ( char *Name, char *String, int Value ) { static int CliSymbolType = LIB$K_CLI_GLOBAL_SYM; static char ValueString [32]; static $DESCRIPTOR (NameDsc, ""); static $DESCRIPTOR (ValueDsc, ""); static $DESCRIPTOR (ValueFaoDsc, "!UL"); static $DESCRIPTOR (ValueStringDsc, ValueString); int status; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "SetSymbol() |%s|%s| %d\n", Name, String, Value); NameDsc.dsc$a_pointer = Name; NameDsc.dsc$w_length = strlen(Name); if (!String) { ValueDsc.dsc$a_pointer = ValueString; sys$fao (&ValueFaoDsc, &ValueDsc.dsc$w_length, &ValueStringDsc, Value); ValueString[ValueDsc.dsc$w_length] = '\0'; } else { ValueDsc.dsc$a_pointer = String; if ((ValueDsc.dsc$w_length = strlen(String)) > 255) ValueDsc.dsc$w_length = 255; } if (Debug) fprintf (stdout, "|%s| %d\n", Name, ValueDsc.dsc$w_length); if (VMSnok (status = lib$set_symbol (&NameDsc, &ValueDsc, &CliSymbolType))) exit (status); } /*****************************************************************************/ /* */ char* TimeString () { static int LibDayOfWeek = LIB$K_DAY_OF_WEEK; static char *WeekDays [] = {"","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"}; static char TimeString [35]; static $DESCRIPTOR (DayDateTimeDsc, "!AZ, !%D"); static $DESCRIPTOR (TimeStringDsc, TimeString); int status, DayOfWeek; unsigned short Length; /*********/ /* begin */ /*********/ lib$cvt_from_internal_time (&LibDayOfWeek, &DayOfWeek, &NowTime64); sys$fao (&DayDateTimeDsc, &Length, &TimeStringDsc, WeekDays[DayOfWeek], &NowTime64); TimeString[Length-3] = '\0'; return (TimeString); } /*****************************************************************************/ /* Return a pointer to a string containing a representative bytes-per-second. */ char* BytesPerString (unsigned int BytesPerSecond) { static char BytesPerString [32]; /*********/ /* begin */ /*********/ /* Prevent "%CC-E-INTCONST, Ill-formed integer constant" under Compaq C V6.4-005 on OpenVMS VAX V7.2 (at least). */ if (BytesPerSecond >= (10000000 * 1000)) sprintf (BytesPerString, "%.1fGB/s", (float)BytesPerSecond/1000000000.0); else if (BytesPerSecond >= 100000000) sprintf (BytesPerString, "%dfMB/s", BytesPerSecond/1000000); else if (BytesPerSecond >= 1000000) sprintf (BytesPerString, "%.1fMB/s", (float)BytesPerSecond/1000000.0); else if (BytesPerSecond >= 100000) sprintf (BytesPerString, "%dkB/s", BytesPerSecond/1000); else if (BytesPerSecond >= 1000) sprintf (BytesPerString, "%.1fkB/s", (float)BytesPerSecond/1000.0); else sprintf (BytesPerString, "%dB/s", BytesPerSecond); return (BytesPerString); } /*****************************************************************************/ /* Convert the 32/64 bit integer (depending on architecture) pointed to into an ASCII number string containing commas. The destination string should contain capacity of a minimum 16 characters for 32 bits, or 32 characters for 64 bits. */ CommaNumber ( int Bits, unsigned long Value, char *String ) { static $DESCRIPTOR (Value32FaoDsc, "!UL"); static $DESCRIPTOR (Value64FaoDsc, "!@SQ"); int cnt, status; unsigned short Length; double dValue; char *cptr, *sptr; char Scratch [32]; $DESCRIPTOR (ScratchDsc, Scratch); /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "CommaNumber()\n"); if (Bits > 32) #ifdef __VAX { /* a bit of a kludge but apparently ok all the way up to 53 bits */ dValue = (double)((unsigned long*)Value)[0]; dValue += (double)((unsigned long*)Value)[1] * 4294967296.0; Length = sprintf (Scratch, "%.0f", dValue); status = SS$_NORMAL; } #else /* Alpha or IA64 */ status = sys$fao (&Value64FaoDsc, &Length, &ScratchDsc, Value); #endif /* #ifdef __VAX */ else status = sys$fao (&Value32FaoDsc, &Length, &ScratchDsc, Value); if (VMSnok (status)) { strcpy (String, "*ERROR*"); return; } Scratch[Length] = '\0'; if (((Length-1) / 3) < 1) { strcpy (String, Scratch); return; } else if (!(cnt = Length % 3)) cnt = 3; cptr = Scratch; sptr = String; while (*cptr) { if (!cnt--) { *sptr++ = ','; cnt = 2; } *sptr++ = *cptr++; } *sptr = '\0'; } /*****************************************************************************/ /* From [SRC.HTTPD]SUPPORT.C Support version 10 and pre-version-10 logical and file naming conventions. Look for one of multiple (usually just two but can be more) possible logical names. When one is successfully translated return a pointer to it's null terminated name. Otherwise test for a subsequent and return a pointer to it if found. If none is found then return NoneFound if zero or positive, return the LogicalName if minus one. The constants should be #defined in the manner of "?WASD_CONFIG_GLOBAL\0HTTPD$CONFIG\0". */ char* v10orPrev10 ( char *LogicalName, int NoneFound ) { static unsigned short Length; static char ValueString [128]; static $DESCRIPTOR (LogicalNameDsc, ""); static $DESCRIPTOR (LnmFileDevDsc, "LNM$FILE_DEV"); static struct { unsigned short buf_len; unsigned short item; void *buf_addr; void *ret_len; } LnmItems [] = { { sizeof(ValueString)-1, LNM$_STRING, ValueString, &Length }, { 0,0,0,0 } }; int status; char *cptr, *sptr; /*********/ /* begin */ /*********/ if (!LogicalName || LogicalName[0] != '?') return (LogicalName); /* stop at any logical name delimiting colon (perhaps of a file name) */ for (sptr = cptr = LogicalName+1; *sptr && *sptr != ':'; sptr++); while (*cptr) { LogicalNameDsc.dsc$a_pointer = cptr; LogicalNameDsc.dsc$w_length = sptr - cptr; status = sys$trnlnm (0, &LnmFileDevDsc, &LogicalNameDsc, 0, &LnmItems); if (VMSok (status)) return (cptr); /* scan past any logical-delimiting colon */ while (*sptr) sptr++; /* stop at any logical name delimiting colon (perhaps of a file name) */ for (cptr = (sptr += 1); *sptr && *sptr != ':'; sptr++); } if (NoneFound != -1) return ((char*)NoneFound); return (LogicalName+1); } /*****************************************************************************/ /* Get "command-line" parameters, whether from the command-line or from a configuration logical containing the equivalent. */ GetParameters () { static char CommandLine [256]; static unsigned long Flags = 0; int status; unsigned short Length; char ch; char *aptr, *cptr, *clptr, *sptr, *zptr; $DESCRIPTOR (CommandLineDsc, CommandLine); /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "GetParameters()\n"); if (!(clptr = getenv ("HTTPDMON$PARAM"))) { /* get the entire command line following the verb */ if (VMSnok (status = lib$get_foreign (&CommandLineDsc, 0, &Length, &Flags))) exit (status); (clptr = CommandLine)[Length] = '\0'; } aptr = NULL; ch = *clptr; for (;;) { if (aptr && *aptr == '/') *aptr = '\0'; if (!ch) break; *clptr = ch; if (Debug) fprintf (stdout, "clptr |%s|\n", clptr); while (*clptr && isspace(*clptr)) *clptr++ = '\0'; aptr = clptr; if (*clptr == '/') clptr++; while (*clptr && !isspace (*clptr) && *clptr != '/') { if (*clptr != '\"') { clptr++; continue; } cptr = clptr; clptr++; while (*clptr) { if (*clptr == '\"') if (*(clptr+1) == '\"') clptr++; else break; *cptr++ = *clptr++; } *cptr = '\0'; if (*clptr) clptr++; } ch = *clptr; if (*clptr) *clptr = '\0'; if (Debug) fprintf (stdout, "aptr |%s|\n", aptr); if (!*aptr) continue; if (strsame (aptr, "/ALERT=", 4)) { sptr = aptr; while (*aptr && *aptr != '=') aptr++; if (*aptr) aptr++; while (*aptr) { if (!*aptr || strsame (aptr, "ALL", 3)) { DoAlertAll = true; DoAlertHost = false; } else if (strsame (aptr, "HOST", 4)) { DoAlertAll = false; DoAlertHost = true; } else if (strsame (aptr, "PATH", 4)) { DoAlertAll = false; DoAlertPath = true; while (*aptr && *aptr != '=') aptr++; if (*aptr) { aptr++; PathAlertBellRepetition = atoi(aptr); } } else if (strsame (aptr, "NOSERVERS", 9)) DoAlertServers = false; else if (strsame (aptr, "SERVERS", 7)) DoAlertServers = true; else { fprintf (stdout, "%%%s-E-INVPARM, invalid parameter\n \\%s\\\n", Utility, sptr); exit (STS$K_ERROR | STS$M_INHIB_MSG); } while (*aptr && *aptr != ',') aptr++; if (*aptr) aptr++; } continue; } if (strsame (aptr, "/ALL=", 4)) { sptr = aptr; while (*aptr && *aptr != '=') aptr++; if (*aptr) aptr++; if (!isdigit(*aptr)) { fprintf (stdout, "%%%s-E-INVPARM, invalid parameter\n \\%s\\\n", Utility, sptr); exit (STS$K_ERROR | STS$M_INHIB_MSG); } InstanceEnvNumber = atoi(aptr); continue; } if (strsame (aptr, "/DBUG", -1)) { Debug = true; continue; } if (strsame (aptr, "/DEMO", -1)) { DemoMode = true; continue; } if (strsame (aptr, "/ENV=", 4)) { sptr = aptr; while (*aptr && *aptr != '=') aptr++; if (*aptr) aptr++; if (!isdigit(*aptr)) { fprintf (stdout, "%%%s-E-INVPARM, invalid parameter\n \\%s\\\n", Utility, sptr); exit (STS$K_ERROR | STS$M_INHIB_MSG); } InstanceEnvNumber = atoi(aptr); continue; } if (strsame (aptr, "/GENERAL", 4)) { DoGeneralInfo = true; DoNoGeneralInfo = false; continue; } if (strsame (aptr, "/NOGENERAL", 6)) { DoGeneralInfo = false; DoNoGeneralInfo = true; continue; } if (strsame (aptr, "/HELP", 4)) { DoShowHelp = true; continue; } if (strsame (aptr, "/IDENTIFICATION=", 3)) { sptr = aptr; while (*aptr && *aptr != '=') aptr++; if (*aptr) aptr++; if (!isxdigit(*aptr)) { fprintf (stdout, "%%%s-E-INVPARM, invalid parameter\n \\%s\\\n", Utility, sptr); exit (STS$K_ERROR | STS$M_INHIB_MSG); } sscanf (aptr, "%x", &CliInstancePid); continue; } if (strsame (aptr, "/INTERVAL=", 4) || strsame (aptr, "/REFRESH=", 4)) { sptr = aptr; while (*aptr && *aptr != '=') aptr++; if (*aptr) aptr++; if (!isdigit(*aptr)) { fprintf (stdout, "%%%s-E-INVPARM, invalid parameter\n \\%s\\\n", Utility, sptr); exit (STS$K_ERROR | STS$M_INHIB_MSG); } IntervalSeconds = atoi(aptr); continue; } if (strsame (aptr, "/LOOKUP", 4)) { DoLookupHost = true; continue; } if (strsame (aptr, "/NOLOOKUP", 6)) { DoLookupHost = false; continue; } if (strsame (aptr, "/PLUCK", 5)) { DoPluck = true; continue; } if (strsame (aptr, "/PORT=", 4)) { sptr = aptr; while (*aptr && *aptr != '=') aptr++; if (*aptr) aptr++; if (!isdigit(*aptr)) { fprintf (stdout, "%%%s-E-INVPARM, invalid parameter\n \\%s\\\n", Utility, sptr); exit (STS$K_ERROR | STS$M_INHIB_MSG); } ServerPort = atoi(aptr); continue; } if (strsame (aptr, "/PROCESS", 5)) { DoProcessInfo = true; DoNoProcessInfo = false; continue; } if (strsame (aptr, "/NOPROCESS", 7)) { DoProcessInfo = false; DoNoProcessInfo = true; continue; } if (strsame (aptr, "/PROXY", 5)) { DoProxyInfo = true; DoNoProxyInfo = false; continue; } if (strsame (aptr, "/NOPROXY", 7)) { DoProxyInfo = false; DoNoProxyInfo = true; continue; } if (strsame (aptr, "/REQUEST", 4)) { DoRequestInfo = true; DoNoRequestInfo = false; continue; } if (strsame (aptr, "/NOREQUEST", 6)) { DoRequestInfo = false; DoNoRequestInfo = true; continue; } if (strsame (aptr, "/STATUS", 4)) { DoStatusInfo = true; DoNoStatusInfo = false; continue; } if (strsame (aptr, "/NOSTATUS", 6)) { DoStatusInfo = false; DoNoStatusInfo = true; continue; } if (*aptr != '/') { fprintf (stdout, "%%%s-E-MAXPARM, too many parameters\n \\%s\\\n", Utility, aptr); exit (STS$K_ERROR | STS$M_INHIB_MSG); } else { fprintf (stdout, "%%%s-E-IVQUAL, unrecognized qualifier\n \\%s\\\n", Utility, aptr+1); exit (STS$K_ERROR | STS$M_INHIB_MSG); } } } /****************************************************************************/ /* */ int ShowHelp () { fprintf (stdout, "%%%s-I-HELP, usage for the WASD HTTPd Monitor (%s)\n\ \n\ Continuously displays the status of an HTTPd process (must be executed on the\n\ system that has the process running on it, defaulting to port 80). Provides\n\ process information, server counters, latest request information, with proxy\n\ processing statistics optionally available. By default attempts to resolve\n\ request dotted-decimal host address to host name. The alert qualifier\n\ activates the terminal bell for an increase in server connect count (=ALL)\n\ or change in requesting host (=HOST), or mapped alert path hit (=PATH).\n\ \n\ $ HTTPDMON [qualifiers...]\n\ \n\ /ALERT[=ALL|HOST|PATH[=]] /DEMO /ENV= /[NO]GENERAL /HELP\n\ /INTERVAL= /[NO]LOOKUP /PLUCK /PRCNAM=prcnam /PORT=\n\ /[NO]PROCESS /[NO]PROXY /REFRESH=integer /[NO]REQUEST /[NO]STATUS\n\ \n\ Usage examples:\n\ \n\ $ HTTPDMON\n\ $ HTTPDMON /INTERVAL=15 /PROXY\n\ $ HTTPDMON /PORT=8080 /ALERT\n\ $ HTTPDMON /NOLOOKUP\n\ \n", Utility, SOFTWAREID); return (SS$_NORMAL); } /****************************************************************************/ /* Does a case-insensitive, character-by-character string compare and returns true if two strings are the same, or false if not. If a maximum number of characters are specified only those will be compared, if the entire strings should be compared then specify the number of characters as 0. */ BOOL strsame ( char *sptr1, char *sptr2, int count ) { while (*sptr1 && *sptr2) { if (toupper (*sptr1++) != toupper (*sptr2++)) return (false); if (count) if (!--count) return (true); } if (*sptr1 || *sptr2) return (false); else return (true); } /*****************************************************************************/