/*****************************************************************************/ /* 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 /[NO]GEOLOCATE[=] control optional geolocation /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 With optional geolocation, BUILD_HTTPDMON_GEOLOCATE.COM COPYRIGHT --------- Copyright (C) 1996-2024 Mark G.Daniel Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. VERSION HISTORY (update SOFTWAREVN as well!) --------------- 20-AUG-2024 MGD v3.2.0, proxy FTP obsolete 19-JUN-2024 MGD v3.1.2, () all extra ->MethodCount 13-JAN-2024 MGD v3.1.1, compiler-fix; when function and (stack?) storage share identical names VSI C x86-64 V7.5-009 (GEM 50XBR) on OpenVMS x86_64 V9.2-1 G2L-E-ASSERT, GEM ENTRY TimeString ... defined as GEM VARIABLE 02-FEB-2022 MGD v3.1.0, optional geolocation bugfix; TcpIpLookup() ares[aCacheIdx] 06-MAY-2021 MGD v3.0.0, WASD v12.n.n so long, farewell, Auf Wiedersehen, goodnight (-VAX) 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 "v3.2.0" #define SOFTWARENM "HTTPDMON" #ifdef __ALPHA # define SOFTWAREID SOFTWARENM " " SOFTWAREVN " AXP" #endif #ifdef __ia64 # define SOFTWAREID SOFTWARENM " " SOFTWAREVN " IA64" #endif #ifdef __x86_64 # define SOFTWAREID SOFTWARENM " " SOFTWAREVN " X86" #endif /* standard C header files */ #include #include #include #include /* VMS related header files */ #include #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 */ /* ensure BSD 4.4 structures */ #define _SOCKADDR_LEN /* BUT MultiNet BG driver does not support BSD 4.4 AF_INET addresses */ #define NO_SOCKADDR_LEN_4 #include #include #include #include #ifdef HTTPDMON_GEOLOCATE #include "geolocate.h" #endif #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 [2] = { 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; int64 NowTime64; char CliGeoLocate [256], 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[0K", ANSIceos [] = "\x1b[0J", 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 CommaNumber (int, void*, char*); 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); void ThisLongAgo (int64*, char*); char* TimeString (); void TryIt (char*); 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 (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 (); if (DoPluck) exit (PluckStats ()); 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 (Debug) fprintf (stdout, "%d %d\n", 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 int64 ConnectTime64, JpiLoginTime64; 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(JpiLoginTime64), JPI$_LOGINTIM, &JpiLoginTime64, 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); ConnectTime64 = JpiLoginTime64 - NowTime64; sys$fao (&UpTimeFaoDsc, &Length, &UpTimeDsc, &ConnectTime64); 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->BytesRawRx64, BytesRawRx); CommaNumber (64, &AccountingPtr->BytesRawTx64, 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->RequestSSLCount, PercentOf(AccountingPtr->RequestSSLCount, AccountingPtr->ProcessingTotalCount[HTTP12]), 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, AccountingPtr->MethodWebDavCopyCount + AccountingPtr->MethodWebDavLockCount + AccountingPtr->MethodWebDavMkColCount + AccountingPtr->MethodWebDavMoveCount + AccountingPtr->MethodWebDavPropFindCount + AccountingPtr->MethodWebDavPropPatchCount + AccountingPtr->MethodWebDavUnLockCount + 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; int64 BytesRaw64, BytesTotal64; char BytesRawRx [32], BytesRawTx [32], ConnectString [128]; char *cptr, *sptr, *EnabledPtr; /*********/ /* 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"; /*********/ /* bytes */ /*********/ status = lib$addx (&HttpdGblSecPtr->ProxyAccounting.BytesRawRx64, &HttpdGblSecPtr->ProxyAccounting.BytesRawTx64, &BytesRaw64, 0); if (Debug) fprintf (stdout, "lib$addx() %%X%08.08X\n", status); CommaNumber (64, &HttpdGblSecPtr->ProxyAccounting.BytesRawRx64, BytesRawRx); CommaNumber (64, &HttpdGblSecPtr->ProxyAccounting.BytesRawTx64, BytesRawTx); CountTotal = HttpdGblSecPtr->ProxyAccounting.ConnectIpv4Count + HttpdGblSecPtr->ProxyAccounting.ConnectIpv6Count; /***********/ /* 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\ %sSOCKS5:%s Count:%d Success:%d Fail:%d\ %s\r\n\ %sRework:%s Count:%d No.Type:%d Search:%d Replace:%d Too.Big:%d\ %s\r\n\ %sNetwork:%s Rx:%s Tx:%s\ %s\r\n\ %sLookup:%s Literal:%d DNS:%d Cache:%d Error:%d\ %s\r\n", ANSIceol, ANSIbold, ANSInormal, EnabledPtr, ConnectString, ANSIceol, ANSIbold, ANSInormal, HttpdGblSecPtr->ProxyAccounting.Socks5Count, HttpdGblSecPtr->ProxyAccounting.Socks5SuccessCount, HttpdGblSecPtr->ProxyAccounting.Socks5FailCount, ANSIceol, ANSIbold, ANSInormal, HttpdGblSecPtr->ProxyAccounting.ReworkCount, HttpdGblSecPtr->ProxyAccounting.ReworkNoType, HttpdGblSecPtr->ProxyAccounting.ReworkReplaceSearch, HttpdGblSecPtr->ProxyAccounting.ReworkReplaceCount, HttpdGblSecPtr->ProxyAccounting.ReworkTooBig, ANSIceol, ANSIbold, ANSInormal, BytesRawRx, BytesRawTx, ANSIceol, ANSIbold, ANSInormal, AccountingPtr->LookupLiteralCount, AccountingPtr->LookupDnsNameCount, AccountingPtr->LookupCacheNameCount, AccountingPtr->LookupDnsNameErrorCount, ANSIceol); 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, GeoLength, LineLength, LineLimit, StringLength; double dValue; char *bptr, *bpsptr, *cptr, *sptr, *tptr; char BytesRawRx [32], BytesRawTx [32], BytesTxGzipPercent [8]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "AddRequest()\n"); CommaNumber (64, &HttpdGblSecPtr->Request.BytesRawRx64, BytesRawRx); CommaNumber (64, &HttpdGblSecPtr->Request.BytesRawTx64, BytesRawTx); if (HttpdGblSecPtr->Request.BytesTxGzipPercent) sprintf (BytesTxGzipPercent, " (%d%%)", HttpdGblSecPtr->Request.BytesTxGzipPercent); else BytesTxGzipPercent[0] = '\0'; 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, HttpdGblSecPtr->Request.Duration); bpsptr = BytesPerString (HttpdGblSecPtr->Request.BytesPerSecond); 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] ? ")" : ""); 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 = ""; } BytesLength = strlen(BytesRawRx) + strlen(BytesRawTx) + strlen(BytesTxGzipPercent); StringLength = 41 + 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 (%s)", cptr, bpsptr); sptr += sprintf (sptr, "%s\r\n\ %sHost:%s ", ANSIceol, ANSIbold, ANSInormal); LineLength = 10; cptr = HttpdGblSecPtr->Request.ClientHostName; if (strcmp (cptr, PrevHostName)) { strcpy (PrevHostName, cptr); LookupHostNamePtr = NULL; if (DoAlertHost) fputs ("\x07", stdout); } if (DoAlertPath && HttpdGblSecPtr->Request.Alert) { if (AccountingPtr->PathAlertCount > PrevPathAlertCount) { PrevPathAlertCount = AccountingPtr->PathAlertCount; PathAlertBellCount = PathAlertBellRepetition; } } if (PathAlertBellCount) { fputs ("\x07", stdout); PathAlertBellCount--; } /* check for IPv4 address */ tptr = cptr; /* if an IPv6 compressed IPv4 address */ if (!strncmp (tptr, "::FFFF:", 7)) tptr += 7; while (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 (*(ulong*)(tptr = cptr) == '::FF') tptr += 7; if (DoLookupHost) if (IsIpAddr) if (tptr = LookupHostName (tptr)) if (IsHostName = (*tptr != '?' && *tptr != '!')) cptr = tptr; StringLength = strlen(cptr); StringLength += strlen (HttpdGblSecPtr->Request.ClientIpAddressString); /* excise some middle section if it would overflow the line */ if (StringLength > 80-16) { while (*cptr && --StringLength > 80-16) cptr++; *sptr++ = '8'; *sptr++ = '<'; } while (*cptr) *sptr++ = *cptr++; LineLength += StringLength; if (IsHostName) { tptr = sptr; *sptr++ = ' '; *sptr++ = '('; for (cptr = HttpdGblSecPtr->Request.ClientIpAddressString; *cptr; *sptr++ = *cptr++); *sptr++ = ')'; LineLength += sptr - tptr; } 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++); } LineLimit = ((PageLength - 21) * 80) - 14; #ifdef HTTPDMON_GEOLOCATE if (!CliGeoLocate[0]) { char *cptr, *sptr, *zptr; if (cptr = GeoLocateLogicalName ("HTTPDMON_GEOLOCATE")) { zptr = (sptr = CliGeoLocate) + sizeof(CliGeoLocate)-1; while (*cptr && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; } else CliGeoLocate[0] = '*'; } GeoLength = 0; if (CliGeoLocate[0] && CliGeoLocate[0] != '!') { cptr = HttpdGblSecPtr->Request.ClientIpAddressString; if (isalnum(CliGeoLocate[0])) cptr = GeoLocate (CliGeoLocate, cptr); else if (isalnum(CliGeoLocate[1])) cptr = GeoLocate (CliGeoLocate+1, cptr); else cptr = GeoLocate (NULL, cptr); if (!cptr) cptr = "[null]"; else if (*cptr) cptr = GeoLocateData (CliGeoLocate[0] == '?'); else if (*(cptr+1)) cptr++; else cptr = "[oops]"; GeoLength = strlen(cptr); } if (GeoLength && GeoLength > 76 - LineLength) { sptr += sprintf (sptr, "%s\r\n {%s}", ANSIceol, cptr); LineLimit -= 80; } else if (GeoLength) sptr += sprintf (sptr, " {%s}", cptr); #endif /* HTTPDMON_GEOLOCATE */ sptr += sprintf (sptr, "%s\r\n %sRequest:%s %s", ANSIceol, ANSIbold, ANSInormal, HttpdGblSecPtr->Request.Alert ? ANSIbold : ""); /* limit request string to number of 80 char lines left minus field name */ tptr = sptr + LineLimit; /* 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; char *cptr; char 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 (!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'; MinsAgo = (NowTime64 - istptr->UpdateTime64) / TIME64_ONE_MIN; if (MinsAgo > INSTANCE_STATUS_STALE_MINS) stale = true; else { stale = false; total++; } sprintf (LineBuffer, "%s%-*s %4.4s %4.4s %5u %4.4s %10.10s %7.7s %4u %6u%s\r\n", stale ? " -" : " ", maxlen, istptr->InstanceName, UpdateAgoBuf, StartAgoBuf, istptr->StartupCount, ExitAgoBuf, ExitStatusBuf, istptr->HttpdVersion, istptr->MinuteCount, istptr->HourCount, stale ? "-" : ""); if (stale) for (cptr = LineBuffer + 2; *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. */ void ThisLongAgo ( int64 *Time64Ptr, char *AgoBuf ) { static ulong LibDeltaSecs = LIB$K_DELTA_SECONDS; static $DESCRIPTOR (AgoFaoDsc, "!UL!AZ\0"); static $DESCRIPTOR (AgoBufDsc, ""); int status; ulong SecsAgo; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ThisLongAgo()\n"); AgoBuf[0] = '\0'; if (!*Time64Ptr) return (SS$_NORMAL); SecsAgo = (NowTime64 - *Time64Ptr) / TIME64_ONE_SEC; AgoBufDsc.dsc$a_pointer = AgoBuf; AgoBufDsc.dsc$w_length = 16; 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"); } /*****************************************************************************/ /* Development only. */ #define TCPIP_LOOKUP_HOST_NAME_RETRY 3 int AcpControl; char* LookupHostName (ulong arg1) { char *sptr; if (AcpControl) sptr = LookupHostName2 (arg1); else { sptr = TcpIpLookup (NULL, (char*)arg1, NULL, NULL); if (*sptr == '[') return ("!"); } return (sptr); } /*****************************************************************************/ /* If |Name| is non-NULL lookup the IP address using the host name. If |Address| is non-NULL lookup the host name using the address. If the Ip4AddtrPtr and/or Ip6AddrPtr are non-NULL populate them. Return either a pointer to the resolved host name or IP address string or an error message between square brackets. */ char* TcpIpLookup ( char *Name, char *Address, uchar *Ip4Addr, uchar *Ip6Addr ) { #define CACHE_MAX 16 static aCacheIdx, nCacheIdx; static char abuf [CACHE_MAX][256], ares [CACHE_MAX][256], nbuf [CACHE_MAX][256], nres [CACHE_MAX][256]; int idx, retry, retval; void *addptr; struct sockaddr_in addr4; struct sockaddr_in6 addr6; struct addrinfo hints; struct addrinfo *aiptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "TcpIpLookup() |%s|%s| %d %d\n", Name, Address, Ip4Addr, Ip6Addr); if (Ip4Addr) memset (Ip4Addr, 0, 4); if (Ip6Addr) memset (Ip6Addr, 0, 16); if (Name) { for (idx = 0; idx < CACHE_MAX; idx++) if (!strcmp (nbuf[idx], Name)) return (nres[idx]); idx = nCacheIdx++ % CACHE_MAX; snprintf (nbuf[idx], sizeof(nbuf[0]), "%s", Name); aiptr = NULL; memset (&hints, 0, sizeof(hints)); hints.ai_flags |= AI_CANONNAME; retval = 0; for (retry = TCPIP_LOOKUP_HOST_NAME_RETRY; retry; retry--) { retval = getaddrinfo (Name, NULL, &hints, &aiptr); if (retval != EINTR && retval != EAI_AGAIN) break; sleep (1); } if (retval) { if (retval == EAI_NONAME) sprintf (nres[idx], "[unknown]"); else if (retval == EAI_FAIL || retval == EAI_AGAIN) sprintf (nres[idx], "[failed]"); else snprintf (nres[idx], sizeof(nres[0]), "[%s]", gai_strerror(retval)); return (nres[idx]); } else { if (aiptr->ai_family == AF_INET) { /* IPv4 */ addptr = &((struct sockaddr_in *)aiptr->ai_addr)->sin_addr; if (Ip4Addr) memcpy (Ip4Addr, addptr, 4); } else { /* must be IPv6 */ addptr = &((struct sockaddr_in6 *)aiptr->ai_addr)->sin6_addr; if (Ip6Addr) memcpy (Ip6Addr, addptr, 16); } if (!inet_ntop (aiptr->ai_family, addptr, nres[idx], sizeof(nres[0]))) snprintf (nres[idx], sizeof(nres[0]), "[%s]", strerror(errno)); } /* free the addrinfo */ freeaddrinfo(aiptr); return (nres[idx]); } if (Address) { for (idx = 0; idx < CACHE_MAX; idx++) if (!strcmp (abuf[idx], Address)) return (ares[idx]); idx = aCacheIdx++ % CACHE_MAX; strcpy (abuf[idx], Address); retval = 0; memset (&addr4, 0, sizeof(addr4)); if (inet_pton (AF_INET, Address, &addr4.sin_addr) > 0) { /* MultiNet does not support BSD 4.4 AF_INET addresses */ #ifdef NO_SOCKADDR_LEN_4 /* this kludge seems to work for both! */ *(USHORTPTR)&addr4 = AF_INET; #else addr4.sin_len = sizeof(struct sockaddr_in); addr4.sin_family = AF_INET; #endif if (Ip4Addr) memcpy (Ip4Addr, &addr4.sin_addr, sizeof(addr4.sin_addr)); for (retry = TCPIP_LOOKUP_HOST_NAME_RETRY; retry; retry--) { retval = getnameinfo ((struct sockaddr*)&addr4, sizeof(addr4), ares[idx], sizeof(ares[0]), NULL, 0, NI_NAMEREQD); if (retval != EINTR && retval != EAI_AGAIN) break; sleep (1); } if (retval) { if (retval == EAI_NONAME) sprintf (ares[idx], "[unknown]"); else if (retval == EAI_FAIL || retval == EAI_AGAIN) sprintf (ares[idx], "[failed]"); else snprintf (ares[idx], sizeof(ares[0]), "[%s]", gai_strerror(retval)); } return (ares[idx]); } else { memset (&addr6, 0, sizeof(addr6)); if (inet_pton (AF_INET6, Address, &addr6.sin6_addr) > 0) { addr6.sin6_len = sizeof(struct sockaddr_in6); addr6.sin6_family = AF_INET6; if (Ip6Addr) memcpy (Ip6Addr, &addr6.sin6_addr, sizeof(addr6.sin6_addr)); for (retry = TCPIP_LOOKUP_HOST_NAME_RETRY; retry; retry--) { retval = getnameinfo ((struct sockaddr*)&addr6, addr6.sin6_len, ares[idx], sizeof(ares[0]), NULL, 0, NI_NAMEREQD); if (retval != EINTR && retval != EAI_AGAIN) break; sleep (1); } if (retval) { if (retval == EAI_NONAME) sprintf (ares[idx], "[unknown]"); else if (retval == EAI_FAIL || retval == EAI_AGAIN) sprintf (ares[idx], "[failed]"); else snprintf (ares[idx], sizeof(ares[0]), "[%s]", gai_strerror(retval)); } } else snprintf (ares[idx], sizeof(ares[0]), "[%s]", strerror(errno)); return (ares[idx]); } } return ("[bugcheck]"); } /*****************************************************************************/ /* 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). Returns /%X00000014 %SYSTEM-F-BADPARAM, bad parameter value/ when fed an IPv6 address string. 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* LookupHostName2 (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$C_HOSTENT_OFFSET 5 #define INETACP_FUNC$C_GETHOSTBYNAME 1 #define INETACP_FUNC$C_GETHOSTBYADDR 2 #define HOSTENT_SIZE 1024 struct LookupCacheStruct { int RetryCount; ushort HostNameLength; ulong ExpiresStamp; uchar Ip4Address [4]; uchar Ip6Address [16]; char HostName [127+1], IpAddrStr [31+1]; char *HostEntPtr; struct dsc$descriptor HostEntDsc; struct dsc$descriptor HostAddrDsc; IOSBLK LookupIOsb; }; static struct LookupCacheStruct LookupCache [CACHE_MAX]; static $DESCRIPTOR (TcpIpDeviceDsc, "UCX$DEVICE"); static unsigned char ControlSubFunction [4] = { INETACP_FUNC$C_GETHOSTBYADDR, INETACP$C_HOSTENT_OFFSET, 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; struct LookupCacheStruct *lcptr; struct hostent *heptr; /*********/ /* 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++) { lcptr = &LookupCache[idx]; lcptr->HostAddrDsc.dsc$b_class = DSC$K_CLASS_S; lcptr->HostAddrDsc.dsc$b_dtype = DSC$K_DTYPE_T; lcptr->HostEntDsc.dsc$b_class = DSC$K_CLASS_S; lcptr->HostEntDsc.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++) { lcptr = &LookupCache[idx]; if (!MATCH8 (cptr, lcptr->IpAddrStr)) continue; if (strcmp (cptr, lcptr->IpAddrStr)) continue; if (Debug) fprintf (stdout, "|%s|->\n", lcptr->HostName); return (lcptr->HostName); } /* look for an unused or expired entry */ for (idx = 0; idx < CACHE_MAX; idx++) { lcptr = &LookupCache[idx]; if (!lcptr->IpAddrStr[0]) break; if (ThisTimeStamp >= lcptr->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); lcptr = &LookupCache[idx]; lcptr->IpAddrStr[0] = lcptr->HostName[1] = '\0'; if (strchr (cptr, ':')) { if (inet_pton (AF_INET6, cptr, &lcptr->Ip6Address) != 1) { *(ulong*)lcptr->HostName = '!\0\0\0'; lcptr->ExpiresStamp = ThisTimeStamp + RETRY_SECONDS; return (lcptr->HostName); } if (Debug) fprintf (stdout, "IPv6\n"); } else { if (inet_pton (AF_INET, cptr, &lcptr->Ip4Address) != 1) { *(ulong*)lcptr->HostName = '!\0\0\0'; lcptr->ExpiresStamp = ThisTimeStamp + RETRY_SECONDS; return (lcptr->HostName); } if (Debug) fprintf (stdout, "IPv4\n"); } lcptr->HostName[0] = '?'; lcptr->ExpiresStamp = ThisTimeStamp + EXPIRE_SECONDS; zptr = (sptr = lcptr->IpAddrStr) + sizeof(lcptr->IpAddrStr)-1; while (*cptr && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; lcptr->HostAddrDsc.dsc$w_length = sptr - lcptr->IpAddrStr; lcptr->HostAddrDsc.dsc$a_pointer = lcptr->IpAddrStr; lcptr->HostEntPtr = calloc (1, HOSTENT_SIZE); if (!lcptr->HostEntPtr) { *(ulong*)lcptr->HostName = '!\0\0\0'; return (lcptr->HostName); } lcptr->HostEntDsc.dsc$w_length = HOSTENT_SIZE; lcptr->HostEntDsc.dsc$a_pointer = lcptr->HostEntPtr; lcptr->RetryCount = RETRY_ATTEMPTS; } else { /**************/ /* lookup AST */ /**************/ lcptr = &LookupCache[idx = arg1]; status = lcptr->LookupIOsb.iosb$w_status; if (Debug) fprintf (stdout, "sys$qio() %%X%08.08X\n", status); if (VMSok (status)) { /************/ /* resolved */ /************/ heptr = (struct hostent *)lcptr->HostEntPtr; heptr->h_name += (unsigned int)heptr; zptr = (sptr = lcptr->HostName) + sizeof(lcptr->HostName)-1; for (cptr = heptr->h_name; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; if (Debug) fprintf (stdout, "|%s|\n", lcptr->HostName); if (!lcptr->HostName[0]) *(ulong*)lcptr->HostName = '!\0\0\0'; free (lcptr->HostEntPtr); lcptr->HostEntPtr = NULL; return (lcptr->HostName); } if (status == SS$_ENDOFFILE || !lcptr->RetryCount) { sys$gettim (&ThisBinTime); ThisTimeStamp = decc$fix_time (&ThisBinTime); lcptr->ExpiresStamp = ThisTimeStamp + RETRY_SECONDS; *(ulong*)lcptr->HostName = '!\0\0\0'; free (lcptr->HostEntPtr); lcptr->HostEntPtr = NULL; return (lcptr->HostName); } lcptr->RetryCount--; } status = sys$qio (0, LookupChannel, IO$_ACPCONTROL | IO$M_EXTEND /* IPv6 */, &lcptr->LookupIOsb, LookupHostName, idx, &ControlSubFunctionDsc, &lcptr->HostAddrDsc, &lcptr->HostEntDsc.dsc$w_length, &lcptr->HostEntDsc, 0, 0); if (Debug) fprintf (stdout, "[%d] sys$qio() %%X%08.08X\n", idx, status); if (VMSnok(status)) lcptr->IpAddrStr[0] = '\0'; return (lcptr->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 maximum 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 buf [35]; static $DESCRIPTOR (DayDateTimeDsc, "!AZ, !%D"); static $DESCRIPTOR (TimeStringDsc, buf); int status, DayOfWeek; unsigned short Length; /*********/ /* begin */ /*********/ lib$cvt_from_internal_time (&LibDayOfWeek, &DayOfWeek, &NowTime64); sys$fao (&DayDateTimeDsc, &Length, &TimeStringDsc, WeekDays[DayOfWeek], &NowTime64); buf[Length-3] = '\0'; return (buf); } /*****************************************************************************/ /* Return a pointer to a string containing a representative bytes-per-second. */ char* BytesPerString (unsigned int BytesPerSecond) { static char buf [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 >= 10000000000) sprintf (buf, "%.1fGB/s", (float)BytesPerSecond/1000000000.0); else if (BytesPerSecond >= 100000000) sprintf (buf, "%dMB/s", BytesPerSecond/1000000); else if (BytesPerSecond >= 1000000) sprintf (buf, "%.1fMB/s", (float)BytesPerSecond/1000000.0); else if (BytesPerSecond >= 100000) sprintf (buf, "%dkB/s", BytesPerSecond/1000); else if (BytesPerSecond >= 1000) sprintf (buf, "%.1fkB/s", (float)BytesPerSecond/1000.0); else sprintf (buf, "%dB/s", BytesPerSecond); return (buf); } /*****************************************************************************/ /* 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. */ void CommaNumber ( int Bits, void *vptr, 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) status = sys$fao (&Value64FaoDsc, &Length, &ScratchDsc, vptr); else status = sys$fao (&Value32FaoDsc, &Length, &ScratchDsc, *(ulong*)vptr); 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); } /*****************************************************************************/ /* Development purposes only. */ void TryIt (char* string) { int count; char *cptr; /*********/ /* begin */ /*********/ if (strsame (string, "lookup=", 7)) { for (count = 5; count; count--) if (cptr = LookupHostName (string + 7)) { if (*cptr != '?') break; sleep (1); } fprintf (stdout, "%d |%s|\n", count, cptr); } } /*****************************************************************************/ /* 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, "/ACPCONTROL", 4)) { /* development only */ AcpControl = true; 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, "/GEOLOCATE=", 4)) { #ifdef HTTPDMON_GEOLOCATE while (*aptr && *aptr != '=') aptr++; if (*aptr) aptr++; if (!*aptr) CliGeoLocate[0] = '*'; else if (*aptr == '0') CliGeoLocate[0] = '!'; else { zptr = (sptr = CliGeoLocate) + sizeof(CliGeoLocate)-1; while (*aptr && sptr < zptr) *sptr++ = *aptr++; *sptr = '\0'; } #else /* HTTPDMON_GEOLOCATE */ fprintf (stdout, "%%%s-E-IVQUAL, not a compiled option\n \\%s\\\n", Utility, aptr+1); exit (STS$K_ERROR | STS$M_INHIB_MSG); #endif /* HTTPDMON_GEOLOCATE */ continue; } if (strsame (aptr, "/NOGEOLOCATE", 6)) { CliGeoLocate[0] = '!'; 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 (strsame (aptr, "/TRY=", 4)) { /* development purposes only */ sptr = aptr; while (*aptr && *aptr != '=') aptr++; if (*aptr) aptr++; TryIt (aptr); exit (SS$_NORMAL); } 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\n\ /[NO]GEOLOCATION[=] /HELP /INTERVAL= /[NO]LOOKUP /PLUCK\n\ /PRCNAM=prcnam /PORT= /[NO]PROCESS /[NO]PROXY /REFRESH=integer\n\ /[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); } /*****************************************************************************/