/*****************************************************************************/ /* 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-2020 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!) --------------- 07-MAR-2020 MGD v2.8.0, re-map global section when startup detected 09-NOV-2018 MGD v2.7.0, refactor IP address handling 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.8.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' typedef struct STRUCT_IOSBLK { ushort iosb$w_status; ushort iosb$w_bcnt; ulong iosb$l_reserved; } IOSBLK; 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 HttpdPid; 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); int CheckHttpdPid (void); void AddInstanceStatus(); char* LookupHostName (ulong); void MapGlobalSection (void); int PercentOf (unsigned long, unsigned long); int QuadPercentOf (unsigned long*, unsigned long*); int SetSymbol (char*, char*, 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 } }; IOSBLK 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.iosb$w_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.iosb$w_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); MapGlobalSection (); 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); if (CheckHttpdPid() == SS$_NONEXPR) MapGlobalSection (); if (HttpdGblSecPtr) { 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. */ 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 unsigned long InAddr [2] = { 0x200, 0x200 }; static unsigned long RetAddr [2]; static char HttpdGblSecName [32]; static $DESCRIPTOR (HttpdGblSecNameDsc, HttpdGblSecName); static $DESCRIPTOR (HttpdGblSecNameFaoDsc, GBLSEC_NAME_FAO); int status, ByteSize, PageSize; unsigned short ShortLength; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "MapGlobalSection() %08.08X\n", HttpdPid); if (HttpdGblSecPtr) { /* delete global section virtual addresses and remap */ status = sys$deltva (&RetAddr, 0, 0); if (VMSnok (status)) exit (status); HttpdGblSecPtr = NULL; } if (!HttpdGblSecPtr) { 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 monitor previously has been running then just wait */ if (HttpdPid) return; 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)) { if (HttpdPid) fputs ("\n", stdout); fprintf (stdout, "%%%s-E-GBLSEC, global section mismatch, rebuild HTTPDMON?\n", Utility); exit (STS$K_ERROR | STS$M_INHIB_MSG); } HttpdPid = HttpdGblSecPtr->HttpdProcessId; } /*****************************************************************************/ /* Add the HTTPd server process information to the screen buffer. */ 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; 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; IOSBLK 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.iosb$w_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.iosb$w_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.iosb$w_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; BOOL IsHostName, IsIpAddr; 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--; } /* 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++); } IsHostName = !(IsIpAddr = (*tptr == '\0')); if (DoLookupHost) if (IsIpAddr) if (tptr = LookupHostName (cptr)) if (IsHostName = (*tptr != '?' && *tptr != '!')) cptr = tptr; StringLength = strlen(cptr); /* excise some middle section if it would overflow the line */ if (StringLength > 43) { while (*cptr && *cptr != '.') *sptr++ = *cptr++; *sptr++ = '.'; *sptr++ = '.'; while (*cptr && --StringLength > 43) cptr++; while (*cptr && *cptr != '.') cptr++; } while (*cptr) *sptr++ = *cptr++; if (IsHostName) { *sptr++ = ' '; *sptr++ = '('; for (cptr = HttpdGblSecPtr->Request.ClientIpAddressString; *cptr; *sptr++ = *cptr++); *sptr++ = ')'; } 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); } /*****************************************************************************/ /* */ int CheckHttpdPid (void) { static unsigned long JpiPid; static struct { unsigned short buf_len; unsigned short item; unsigned char *buf_addr; void *ret_len; } JpiItems [] = { { sizeof(JpiPid), JPI$_PID, &JpiPid, 0 }, {0,0,0,0} }; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "CheckHttpdPid() %08.08X\n", HttpdPid); return (sys$getjpiw (0, &HttpdPid, 0, &JpiItems, 0, 0, 0)); } /*****************************************************************************/ /* 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); } /*****************************************************************************/ /* 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; IOSBLK 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); } /*****************************************************************************/ /* Get the IP name using asynchronous address-to-name lookup. Despite the implied IPv6 functionality TCP/IP Services lookup only supports IPv4 (sigh, one day perhaps). There will always be a delay in the lookup because this function is called during AST delivery, so buffer the IP addresses and host names. Returns a pointer to the host name, to '?' during lookup, or '!' if not resolvable. */ char* LookupHostName (ulong arg1) { #define CACHE_MAX 128 /* absolute max is 1024 */ #define EXPIRE_SECONDS 300 #define RETRY_ATTEMPTS 5 #define RETRY_SECONDS 60 #define INETACP$C_TRANS 2 #define INETACP_FUNC$C_GETHOSTBYNAME 1 #define INETACP_FUNC$C_GETHOSTBYADDR 2 struct LookupCacheStruct { int RetryCount; ushort HostNameLength; ulong ExpiresStamp; ulong Ip4Address; ulong Ip6Address [4]; char HostName [127+1], IpAddrStr [31+1]; struct dsc$descriptor HostAddressDsc; struct dsc$descriptor HostNameDsc; IOSBLK LookupIOsb; }; static struct LookupCacheStruct LookupCache [CACHE_MAX]; static $DESCRIPTOR (TcpIpDeviceDsc, "UCX$DEVICE"); 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 int CacheUsed; static unsigned short LookupChannel; int idx, status; ulong ThisTimeStamp; ulong ThisBinTime [2]; char *cptr, *sptr, *zptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "LookupHostName() %d\n", arg1); if (!LookupChannel) { /* assign a channel to the internet template device */ status = sys$assign (&TcpIpDeviceDsc, &LookupChannel, 0, 0); if (VMSnok (status)) exit (status); /* initialise the descriptors */ for (idx = 0; idx < CACHE_MAX; idx++) { LookupCache[idx].HostNameDsc.dsc$b_class = DSC$K_CLASS_S; LookupCache[idx].HostNameDsc.dsc$b_dtype = DSC$K_DTYPE_T; LookupCache[idx].HostNameDsc.dsc$w_length = sizeof(LookupCache[idx].HostName)-1; LookupCache[idx].HostNameDsc.dsc$a_pointer = LookupCache[idx].HostName; LookupCache[idx].HostAddressDsc.dsc$b_class = DSC$K_CLASS_S; LookupCache[idx].HostAddressDsc.dsc$b_dtype = DSC$K_DTYPE_T; } } if (arg1 > 1023) { /**************************/ /* lookup this IP address */ /**************************/ sys$gettim (&ThisBinTime); ThisTimeStamp = decc$fix_time (&ThisBinTime); cptr = (char*)arg1; if (Debug) fprintf (stdout, "->|%s|\n", cptr); /* search the cache */ for (idx = 0; idx < CacheUsed; idx++) { if (!MATCH8 (cptr, LookupCache[idx].IpAddrStr)) continue; if (strcmp (cptr, LookupCache[idx].IpAddrStr)) continue; if (Debug) fprintf (stdout, "|%s|->\n", LookupCache[idx].HostName); return (LookupCache[idx].HostName); } /* look for an unused or expired entry */ for (idx = 0; idx < CACHE_MAX; idx++) { if (!LookupCache[idx].IpAddrStr[0]) break; if (ThisTimeStamp >= LookupCache[idx].ExpiresStamp) break; } /* otherwise look for an unresolveable entry */ if (idx >= CACHE_MAX) for (idx = 0; idx < CACHE_MAX; idx++) if (LookupCache[idx].IpAddrStr[0] == '!') break; /* otherwise just cycle through the cache array entries */ if (idx >= CACHE_MAX) idx = 0; if (idx > CacheUsed) CacheUsed = idx; if (Debug) fprintf (stdout, "idx %d %d\n", idx, CacheUsed); LookupCache[idx].IpAddrStr[0] = LookupCache[idx].HostName[1] = '\0'; if (strchr (cptr, ':')) { if (inet_pton (AF_INET6, cptr, &LookupCache[idx].Ip6Address) != 1) { LookupCache[idx].HostName[0] = '!'; LookupCache[idx].ExpiresStamp = ThisTimeStamp + RETRY_SECONDS; return (LookupCache[idx].HostName); } LookupCache[idx].Ip4Address = 0; } else if (inet_pton (AF_INET, cptr, &LookupCache[idx].Ip4Address) != 1) { LookupCache[idx].HostName[0] = '!'; LookupCache[idx].ExpiresStamp = ThisTimeStamp + RETRY_SECONDS; return (LookupCache[idx].HostName); } LookupCache[idx].HostName[0] = '?'; LookupCache[idx].ExpiresStamp = ThisTimeStamp + EXPIRE_SECONDS; zptr = (sptr = LookupCache[idx].IpAddrStr) + sizeof(LookupCache[idx].IpAddrStr)-1; while (*cptr && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; if (LookupCache[idx].Ip4Address) { LookupCache[idx].HostAddressDsc.dsc$w_length = sizeof(LookupCache[idx].Ip4Address); LookupCache[idx].HostAddressDsc.dsc$a_pointer = (char*)&LookupCache[idx].Ip4Address; } else { LookupCache[idx].HostAddressDsc.dsc$w_length = sizeof(LookupCache[idx].Ip6Address); LookupCache[idx].HostAddressDsc.dsc$a_pointer = (char*)&LookupCache[idx].Ip6Address; } LookupCache[idx].RetryCount = RETRY_ATTEMPTS; } else { /**************/ /* lookup AST */ /**************/ idx = arg1; status = LookupCache[idx].LookupIOsb.iosb$w_status; if (Debug) fprintf (stdout, "sys$qio() %%X%08.08X\n", status); if (VMSok (status)) { LookupCache[idx].HostName[LookupCache[idx].HostNameLength] = '\0'; if (Debug) fprintf (stdout, "|%s|\n", LookupCache[idx].HostName); if (!LookupCache[idx].HostName[0]) { LookupCache[idx].HostName[0] = '!'; LookupCache[idx].HostName[1] = '\0'; } return (LookupCache[idx].HostName); } if (status == SS$_ENDOFFILE || !LookupCache[idx].RetryCount--) { sys$gettim (&ThisBinTime); ThisTimeStamp = decc$fix_time (&ThisBinTime); LookupCache[idx].ExpiresStamp = ThisTimeStamp + RETRY_SECONDS; LookupCache[idx].HostName[0] = '!'; LookupCache[idx].HostName[1] = '\0'; return (LookupCache[idx].HostName); } LookupCache[idx].RetryCount--; } status = sys$qio (0, LookupChannel, IO$_ACPCONTROL | IO$M_EXTEND, &LookupCache[idx].LookupIOsb, LookupHostName, idx, &ControlSubFunctionDsc, &LookupCache[idx].HostAddressDsc, &LookupCache[idx].HostNameLength, &LookupCache[idx].HostNameDsc, 0, 0); if (Debug) fprintf (stdout, "[%d] sys$qio() %%X%08.08X\n", idx, status); if (VMSnok(status)) LookupCache[idx].IpAddrStr[0] = '\0'; return (LookupCache[idx].HostName); } /*****************************************************************************/ /* */ 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 */ /*********/ MapGlobalSection (); 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); } /*****************************************************************************/