/*****************************************************************************/
/*
                                Admin.c

Server administration support functions for HTTPd.  Many of the reports and
forms used for revision are produced by functions within the core-function
module.  The control function allow server restart, exit, abort, mapping file
reload, authorization path reload and authentication cache purging.


VERSION HISTORY
---------------
11-FEB-2024  MGD  refine/expand server process log reports
12-SEP-2022  MGD  move onclick=s to addEventListener()s to support
                    content-security-policy: 'strict-dynamic'
09-APR-2022  MGD  AdminMenu() [Request+] report
23-SEP-2021  MGD  AdminServerOutputList()
10-MAR-2021  MGD  accomodate PIPE from WASD_ROOT:[SRC.UTILS]WASTEE.C
27-OCT-2020  MGD  bugfix; AdminMenu() activity hours 672 (ActHours[idx-1])
14-APR-2020  MGD  kludge; Safari (at least) does not honour HTTP cache control
                    headers!  Its back-forwardcache breaks stuff.
14-SEP-2019  MGD  bugfix; AdminParsePath() extraneous OdsParseRelease()
18-JAN-2019  MGD  add 418 to status code statistics
25-AUG-2018  MGD  WatchSystemPlus() et.al.
28-APR-2018  MGD  refactor Admin..() AST delivery
16-DEC-2017  MGD  note decade-old border-collapse bug in Chrome
                  https://bugs.chromium.org/p/chromium/issues/detail?id=356132
                  (Firefox, MS Edge, Safari render correctly)
                  *** appears to be fixed from at least version 68... ***
10-OCT-2017  MGD  SesolaReport..() relax from SSL-only to any service
14-NOV-2015  MGD  excise much of the twenty years of reporting HTML cruft
                  AdminRefresh() allow selection of a page refresh interval
                  lift the GZIP restriction for all but main menu and WATCHing
28-SEP-2014  MGD  AdminLookYouUp() and associated Server Admin item
12-MAR-2014  MGD  "no module involved" datum
30-JAN-2014  MGD  tweaks to menu layout to try and get MSIE 9 to behave
27-JUN-2010  MGD  WebSockReport()
23-AUG-2009  MGD  WasdCss[] and some refinements
26-JUL-2007  MGD  bugfix; AdminMenu() JavaScript doIt() call
02-JUN-2007  MGD  WebDAV and Cluster reports,
                  remove file cache, authorization and proxy items from
                    Server Statistics report (keeping it more-to-the-point)
27-OCT-2006  MGD  AdminReportServerStat() quota percentages in use
23-SEP-2006  MGD  AdminReportServerStat() last exit time
15-JUL-2006  MGD  AdminMenu() instance [Active][Passive] functionality
                    (service item) network connection [Purge][All]
04-JUL-2006  MGD  AdminMenu() status panel (time, connect, request) mods
                    use PercentOf32() for more accurate percentages
26-MAY-2005  MGD  [/DO=] button/field and supporting functionality
03-MAR-2005  MGD  AdminMetaConSource() changes to support search lists
30-NOV-2004  MGD  zero proxy accounting 
12-OCT-2004  MGD  add status info (currently only access log write fail)
                  to Server Admin page (beneath dates and accounting)
28-JUL-2004  MGD  HTTP/1.1 items,
                  server admin reports now have the response header generated
                  after the report body so that the buffer length can be used
                  as a content length (allowing connections to persist)
20-MAY-2004  MGD  remove 'supervisor' from report menu
28-JAN-2004  MGD  make process input/output reports plain-text,
                  service access log report (via LoggingReport()),
                  make some accomodations in reports for our old friend
                  Netscape 3.03/Gold (on VMS) which doesn't render some
                  table formatting correctly (or the way we intended anyway)
26-AUG-2003  MGD  rework access to server configuration files,
                  service directory located authorization databases
15-AUG-2003  MGD  where CDATA constraints make using &#10; entity impossible
                  use a field name of hidden$lf and &#94; substituted for it
22-JUL-2003  MGD  allow for CMKRNL when warning on privileges
08-JUN-2003  MGD  cache statistics include permanent/volatile changes
31-JAN-2002  MGD  no menu difference between category and module WATCHing
12-OCT-2002  MGD  refine metacon reporting
16-JUN-2002  MGD  greater hour granularity to activity report selection
30-MAY-2002  MGD  restart when quiet
17-MAY-2002  MGD  activity reports changed inline with GRAPH.C changes
04-APR-2002  MGD  proxy cache STOP scan
28-DEC-2001  MGD  add 'instance' selection
29-OCT-2001  MGD  PERSONA_MACRO reporting
29-SEP-2001  MGD  instance support
04-AUG-2001  MGD  support module WATCHing
13-MAR-2001  MGD  add throttle and supervisor reports
22-DEC-2000  MGD  HTL list admin (partially implemented by HTADMIN.C),
                  SSL client certificate CA verification
02-OCT-2000  MGD  DETACH now an allowed privilege
13-SEP-2000  MGD  ProxyMainReport() call refined
26-AUG-2000  MGD  consolidated WATCH processing and peek
18-JUN-2000  MGD  "All Services" directives,
                  site log item,
                  service configuration,
                  add a little JavaScript to enhance the admin menu
28-MAY-2000  MGD  use $getjpi ...lm values from server startup
04-MAR-2000  MGD  use FaolToNet(), et.al.
13-JAN-2000  MGD  improve log messages, add OPCOM messaging
30-OCT-1999  MGD  unbundled WATCH functionality to its own module
10-OCT-1999  MGD  show process report,
                  move service statistics report to NET.C module,
                  CacheReport() now only optionally reports cached files
04-SEP-1999  MGD  remove WatchRequestHeader()
11-JUN-1999  MGD  bugfix; WatchFilter()
12-APR-1999  MGD  WatchDataDump() for request and response bodies
10-JAN-1999  MGD  add proxy items to WATCH menu,
                  add proxy report
07-NOV-1998  MGD  WATCH facility
16-MAR-1998  MGD  server abort changed to "exit NOW", added "restart NOW"
05-OCT-1997  MGD  file cache
28-SEP-1997  MGD  request durations
06-SEP-1997  MGD  service list
09-AUG-1997  MGD  message database
07-JUL-1997  MGD  activity report and logging control functions,
                  reworked the control function (it was getting untidy)
08-JUN-1997  MGD  "Other" menu item, with "Clients" and "Scripting" reports,
                  AdminRequestReport() and DclScriptingReport() functions
01-FEB-1997  MGD  new for HTTPd version 4
*/
/*****************************************************************************/

#ifdef WASD_VMS_V7
#undef _VMS__V6__SOURCE
#define _VMS__V6__SOURCE
#undef __VMS_VER
#define __VMS_VER 70000000
#undef __CRTL_VER
#define __CRTL_VER 70000000
#endif

/* standard C header files */
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>

/* VMS related header files */
#include <descrip.h>
#include <jpidef.h>
#include <libdef.h>
#include <libdtdef.h>
#include <lnmdef.h>
#include <prvdef.h>
#include <ssdef.h>
#include <stsdef.h>
#include <syidef.h>

/* application related header files */
#include "wasd.h"

#define WASD_MODULE "ADMIN"

/******************/
/* global storage */
/******************/

/********************/
/* external storage */
/********************/

extern BOOL  CacheEnabled,
             DclScriptDetachProcess,
             DclPersonaServicesAvailable,
             GzipAvailable,
             Http2Enabled,
             HttpdAlignFaultReport,
             InstanceNodeSupervisor,
             LoggingEnabled,
             LoggingFileError,
             NetAcceptEnabled,
             NetInstancePassive,
             NetRejectEnabled,
             OperateWithSysPrv,
             PersonaMacro,
             ProtocolHttpsConfigured,
             SesolaVerifyCAConfigured,
             WebDavEnabled;

extern int  ActivityTotalMinutes,
            CacheEntryCount,
            CacheEntriesMax,
            CacheHits0,
            CacheHits10,
            CacheHits100,
            CacheHits1000,
            CacheHits1000plus,
            CacheHitCount,
            CacheLoadCount,
            CacheMemoryInUse,
            CachePermEntryCount,
            CachePermMemoryInUse,
            CacheTotalKBytesMax,
            ControlStringSize,
            EfnWait,
            ExitStatus,
            GblPageCount,
            GblPagePermCount,
            GblSectionCount,
            GblSectionPermCount,
            GzipFindImageStatus,
            HttpdAlignFaultCount,
            HttpdTickSecond,
            InstanceEnvNumber,
            InstanceNodeCurrent,
            InstanceNumber,
            NoticeInvalid,
            OpcomMessages,
            RequestHistoryMax;

extern int64  HttpdStartTime64;

extern char  BuildInfo[],
             CommandLine[],
             ErrorSanityCheck[],
             HttpdScriptAsUserName[],
             InConfigFileName[],
             ServerHostPort[],
             SoftwareID[],
             TcpIpAgentInfo[],
             TimeGmtString[],
             WatchQuotasAlert[];

extern char  *AuthConfigHtaDirectory,
             *GzipZlibNamePtr,
             *GzipZlibVersionPtr;

extern unsigned long  SysPrvMask[];

#define acptr AccountingPtr
extern ACCOUNTING_STRUCT  *AccountingPtr;
extern ACTIVITY_GBLSEC  *ActivityGblSecPtr;
extern CONFIG_STRUCT  Config;
extern HTTPD_GBLSEC  *HttpdGblSecPtr;
extern HTTPD_PROCESS  HttpdProcess;
extern MSG_STRUCT  Msgs;
extern MAPPING_META  *MappingMetaPtr;
extern PROXY_ACCOUNTING_STRUCT  *ProxyAccountingPtr;
extern LIST_HEAD  ServiceList;
extern SYS_INFO  SysInfo;
extern TCPIP_HOST_CACHE  *TcpIpHostCachePtr;
extern WATCH_STRUCT  Watch;

/*****************************************************************************/
/*
Begin HTTPd server administration.
*/ 

void AdminBegin (REQUEST_STRUCT *rqptr)

{
   BOOL  ClusterDo,
         SlashDo,
         UseServerConfig;
   int  status;
   unsigned short  Length;
   char  *cptr, *pinptr, *uriptr;
   char  DoThis [128],
         FieldName [128],
         FieldValue [256],
         FilePath [ODS_MAX_FILE_NAME_LENGTH+1],
         Http2Connection [33],
         Md5HashHexString [33],
         NumberString [16],
         Path [ODS_MAX_FILE_NAME_LENGTH+1],
         ProcessIdString [16],
         ProcessIdUserName [13],
         RefreshInterval [16],
         VirtualHostPort [128];

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD__OTHER))
      WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER, "AdminBegin()");

   if (WATCHING (rqptr, WATCH_RESPONSE))
      WatchThis (WATCHITM(rqptr), WATCH_RESPONSE,
                "ADMIN !AZ", rqptr->rqHeader.PathInfoPtr);

   if (!rqptr->AccountingDone++)
      InstanceGblSecIncrLong (&acptr->DoServerAdminCount);

   if (!rqptr->RemoteUser[0])
   {
      rqptr->rqResponse.HttpStatus = 403;
      ErrorGeneral (rqptr, MsgFor(rqptr,MSG_AUTH_REQUIRED), FI_LI);
      AdminEnd (rqptr);
      return;
   }

   if (!rqptr->AdminTaskPtr)
      rqptr->AdminTaskPtr = VmGetHeap (rqptr, sizeof(ADMIN_TASK));

   FaoBigNumber (NULL);

   pinptr = rqptr->rqHeader.PathInfoPtr;
   uriptr = rqptr->rqHeader.RequestUriPtr;

   /* for admin activities supply a full error report regardless */
   rqptr->rqPathSet.ReportType = ERROR_REPORT_DETAILED;

   /* disable GZIP compression for the main menu and when WATCHing */
   if (strsame (uriptr, HTTPD_ADMIN, -1))
      rqptr->rqResponse.NoGzip = true;
   else
   if (strsame (uriptr, ADMIN_REPORT_WATCH, -1))
      rqptr->rqResponse.NoGzip = true;

   /*********************/
   /* per-service admin */
   /*********************/

#ifdef NOT_FULLY_IMPLEMENTED_YET

   if (strsame (pinptr, HTTPD_VS_ADMIN, -1))
   {
      AdminVirtualServerMenu (rqptr);
      return;
   }

   if (strsame (pinptr, ADMIN_VS_REVISE_HTA, sizeof(ADMIN_VS_REVISE_HTA)-1))
   {
      if (VMSnok (status = AdminParsePath (rqptr, NULL)))
         AdminEnd (rqptr);
      else
         HTAdminBegin (rqptr);
      return;
   }

   if (strsame (pinptr, ADMIN_VS_REVISE_HTL, sizeof(ADMIN_VS_REVISE_HTL)-1))
   {
      if (VMSnok (status = AdminParsePath (rqptr, NULL)))
         AdminEnd (rqptr);
      else
      if (rqptr->rqHeader.Method == HTTP_METHOD_POST)
         PutBegin (rqptr, &AdminEnd);
      else
      if ((pinptr+sizeof(ADMIN_VS_REVISE_HTL)-1)[0])
         if (rqptr->rqHeader.QueryStringLength)
            HTAdminBegin (rqptr);
         else
            FileBegin (rqptr, &AdminEnd, NULL, NULL,
                       rqptr->ParseOds.ExpFileName, "text/plain");
      else
         HTAdminBegin (rqptr);
      return;
   }

   if (strsame (pinptr, ADMIN_VS_CONTROL, sizeof(ADMIN_VS_CONTROL)-1))
   {
      AdminControl (rqptr, NULL, true);
      return;
   }

#endif /** NOT_FULLY_IMPLEMENTED_YET **/

   /********************************************/
   /* modules that parse their own query field */
   /********************************************/

   if (strsame (pinptr, ADMIN_REVISE_HTA, sizeof(ADMIN_REVISE_HTA)-1))
   {
      if (VMSnok (status = AdminParsePath (rqptr, NULL)))
         AdminEnd (rqptr);
      else
         HTAdminBegin (rqptr);
      return;
   }

   if (strsame (pinptr, ADMIN_REVISE_HTL, sizeof(ADMIN_REVISE_HTL)-1))
   {
      if (VMSnok (status = AdminParsePath (rqptr, NULL)))
         AdminEnd (rqptr);
      else
      if (rqptr->rqHeader.Method == HTTP_METHOD_POST)
         PutBegin (rqptr, &AdminEnd);
      else
      if ((pinptr+sizeof(ADMIN_REVISE_HTL)-1)[0])
         if (rqptr->rqHeader.QueryStringLength)
            HTAdminBegin (rqptr);
         else
            FileBegin (rqptr, &AdminEnd, NULL, NULL,
                       rqptr->ParseOds.ExpFileName, "text/plain");
      else
         HTAdminBegin (rqptr);
      return;
   }

   if (strsame (pinptr, ADMIN_REVISE_SITELOG, sizeof(ADMIN_REVISE_SITELOG)-1))
   {
      if (VMSnok (status = AdminParsePath (rqptr, NULL)))
         AdminEnd (rqptr);
      else
      if (rqptr->rqHeader.Method == HTTP_METHOD_POST)
         PutBegin (rqptr, &AdminEnd);
      else
      if (rqptr->rqHeader.QueryStringLength)
         UpdBegin (rqptr, &AdminEnd);
      else
         FileBegin (rqptr, &AdminEnd, NULL, NULL,
                    rqptr->ParseOds.ExpFileName, "text/plain");
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_ACTIVITY, -1))
   {
      GraphActivityReport (rqptr);
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_MATCH, -1))
   {
      StringMatchReport (rqptr);
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_PROCESS, -1))
   {
      WatchProcessReport (rqptr);
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_WATCH, -1))
   {
      if (rqptr->rqHeader.QueryStringLength)
         WatchBegin (rqptr);
      else
         WatchReport (rqptr);
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_WATCH_STRUCT, -1))
   {
      WatchReportStruct (rqptr);
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_WEBDAV, -1))
   {
      DavWebReport (rqptr);
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_WEBDAV_XML, -1))
   {
      DavXmlReport (rqptr);
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_WEBSOCKET, -1))
   {
      WebSockReport (rqptr);
      return;
   }

   if (strsame (pinptr, ADMIN_CONTROL_DELETE_PROCESS, -1))
   {
      WatchDeleteProcess (rqptr);
      return;
   }

   /**********************/
   /* parse query string */
   /**********************/

   ClusterDo = SlashDo = false;
   UseServerConfig = true;
   DoThis[0] = FilePath[0] = Http2Connection[0] = Md5HashHexString[0] =
      NumberString[0] = Path[0] = ProcessIdString[0] = ProcessIdUserName[0] =
      RefreshInterval[0] = VirtualHostPort[0] = '\0';

   if (rqptr->rqHeader.QueryStringLength)
      cptr = rqptr->rqHeader.QueryStringPtr;
   else
      cptr = "";
   while (*cptr)
   {
      status = StringParseQuery (&cptr, FieldName, sizeof(FieldName),
                                        FieldValue, sizeof(FieldValue));
      if (VMSnok (status))
      {
         /* error occured */
         if (status == SS$_IVCHAR) rqptr->rqResponse.HttpStatus = 400;
         rqptr->rqResponse.ErrorTextPtr = "parsing query string";
         ErrorVmsStatus (rqptr, status, FI_LI);
         AdminEnd (rqptr);
         return;
      }

      if (strsame (FieldName, "at", -1) ||
          strsame (FieldName, "this", -1))
         strzcpy (NumberString, FieldValue, sizeof(NumberString));
      else
      if (strsame (FieldName, "cxr", -1))
      {
         /* 'cxr' can be generated from an UPD edit window */
         strzcpy (DoThis, "edit", sizeof(DoThis));
      }
      else
      if (strsame (FieldName, "do", -1))
         strzcpy (DoThis, FieldValue, sizeof(DoThis));
      else
      if (strsame (FieldName, "edit", -1))
         strzcpy (FilePath, FieldValue, sizeof(FilePath));
      else
      if (strsame (FieldName, "entry", -1))
         strzcpy (Md5HashHexString, FieldValue, sizeof(Md5HashHexString));
      else
      if (strsame (FieldName, "file", -1))
      {
         if (to_upper(FieldValue[0]) == 'Y')
            UseServerConfig = false;
         else
         if (to_upper(FieldValue[0]) == 'N')
            UseServerConfig = true;
      }
      else
      if (strsame (FieldName, "http2", -1))
         strzcpy (Http2Connection, FieldValue, sizeof(Http2Connection));
      else
      if (strsame (FieldName, "refresh", -1))
         strzcpy (RefreshInterval, FieldValue, sizeof(RefreshInterval));
      else
      if (strsame (FieldName, "puser", -1))
         strzcpy (ProcessIdUserName, FieldValue, sizeof(ProcessIdUserName));
      else
      if (strsame (FieldName, "path", -1))
         strzcpy (Path, FieldValue, sizeof(Path));
      else
      if (strsame (FieldName, "pid", -1))
         strzcpy (ProcessIdString, FieldValue, sizeof(ProcessIdString));
      else
      if (strsame (FieldName, "puser", -1))
         strzcpy (ProcessIdUserName, FieldValue, sizeof(ProcessIdUserName));
      else
      if (strsame (FieldName, "scope", -1))
      {
         if (strsame (FieldValue, "cluster", -1)) ClusterDo = true;
      }
      else
      if (strsame (FieldName, "service", -1))
         strzcpy (VirtualHostPort, FieldValue, sizeof(VirtualHostPort));
      else
      if (strsame (FieldName, "server", -1))
      {
         if (to_upper(FieldValue[0]) == 'Y')
            UseServerConfig = true;
         else
         if (to_upper(FieldValue[0]) == 'N')
            UseServerConfig = false;
      }
      else
      if (strsame (FieldName, "slashdo", -1))
      {
         SlashDo = true;
         strzcpy (DoThis, FieldValue, sizeof(DoThis));
      }
      else
      if (strsame (FieldName, "virtual", -1))
         strzcpy (VirtualHostPort, FieldValue, sizeof(VirtualHostPort));
      else
      {
         rqptr->rqResponse.HttpStatus = 400;
         ErrorGeneral (rqptr, "Unknown query field.", FI_LI);
         AdminEnd (rqptr);
         return;
      }
   }

   /***************/
   /* do function */
   /***************/

   if (SlashDo)
   {
      if (!DoThis[0] || strsame (DoThis, "HELP", -1))
         ControlDoHelp (rqptr);
      else
      if (strsame (DoThis, "STATUS", -1))
         InstanceStatusCliReport (rqptr);
      else
         AdminControl (rqptr, DoThis, ClusterDo);
      return;
   }

   if (strsame (pinptr, HTTPD_ADMIN, -1) ||
       strsame (pinptr, HTTPD_ADMIN_STATUS, -1) ||
       strsame (pinptr, ADMIN_REPORT, -1) ||
       strsame (pinptr, ADMIN_REVISE, -1) ||
       strsame (pinptr, ADMIN_CONTROL, -1))
   {
      /********/
      /* menu */
      /********/

      AdminMenu (rqptr);
      return;
   }

#if ALIGN_FAULT
   if (strsame (pinptr, ADMIN_REPORT_ALIGN, -1))
   {
      HttpdAlignReport (rqptr);
      return;
   }
#endif /* __x86_64 */

   if (strsame (pinptr, ADMIN_REPORT_AUTH_USER, -1))
   {
      AuthCacheReport (rqptr);
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_CACHE, -1))
   {
      CacheReport (rqptr, false);
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_CACHE_ENTRIES, -1))
   {
      if (Md5HashHexString[0])
         CacheReportEntry (rqptr, Md5HashHexString);
      else
         CacheReport (rqptr, true);
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_CLUSTER, -1))
   {
      WatchShowCluster (rqptr);
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_ACCEPT, -1) ||
       strsame (pinptr, ADMIN_REVISE_ACCEPT, -1) ||
       strsame (pinptr, ADMIN_REPORT_REJECT, -1) ||
       strsame (pinptr, ADMIN_REVISE_REJECT, -1))
   {   
      if (VMSnok (status = AdminParsePath (rqptr, NULL)))
         AdminEnd (rqptr);
      else
      if (rqptr->rqHeader.Method == HTTP_METHOD_POST)
         PutBegin (rqptr, &AdminEnd);
      else
      if (rqptr->rqHeader.QueryStringLength)
         if (strsame (DoThis, "edit", -1))
            UpdBegin (rqptr, &AdminEnd);
         else
            NetRejectReportAdmin (rqptr, DoThis);
      else
         FileBegin (rqptr, &AdminEnd, NULL, NULL,
                    rqptr->ParseOds.ExpFileName, "text/plain");
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_CONFIG, -1) ||
       strsame (pinptr, ADMIN_REVISE_CONFIG, -1))
   {
      if (VMSnok (status = AdminParsePath (rqptr, NULL)))
         AdminEnd (rqptr);
      else
      if (rqptr->rqHeader.Method == HTTP_METHOD_POST)
         PutBegin (rqptr, &AdminEnd);
      else
      if (rqptr->rqHeader.QueryStringLength)
      {
         if (strsame (DoThis, "edit", -1))
            UpdBegin (rqptr, &AdminEnd);
         else
         if (strsame (pinptr, ADMIN_REPORT_CONFIG, -1))
            ConfigReport (rqptr, UseServerConfig);
         else
            ConfigRevise (rqptr, UseServerConfig);
      }
      else
         FileBegin (rqptr, &AdminEnd, NULL, NULL,
                    rqptr->ParseOds.ExpFileName, "text/plain");
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_DCL, -1))
   {
      DclScriptingReport (rqptr);
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_HOSTCACHE, -1))
   {
      TcpIpHostCacheReport (rqptr);
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_PROCESS, -1))
   {
      WatchShowProcess (rqptr, ProcessIdString, ProcessIdUserName);
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_DECNET, -1))
   {
      DECnetScriptingReport (rqptr);
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_HTTP, -1))
   {
      Http2Report (rqptr);
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_HPACK, -1))
   {
      HpackReport (rqptr, Http2Connection);
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_LOCK, -1))
   {
      InstanceLockReport (rqptr);
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_PROCESS_OUTPUT_NOTICED, -1))
   {
      AdminServerOutputNoticed (rqptr);
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_PROCESS_INPUT, -1))
   {
      AdminServerInput (rqptr);
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_PROCESS_OUTPUT_RAW, -1))
   {
      AdminServerOutputRaw (rqptr);
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_PROCESS_OUTPUT_DELTA, -1))
   {
      AdminServerOutputFilter (rqptr);
      return;
   }

   if (strsame (pinptr,
                ADMIN_REPORT_PROCESS_OUTPUT_LIST,
                sizeof(ADMIN_REPORT_PROCESS_OUTPUT_LIST)-1))
   {
      AdminServerOutputList (rqptr);
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_WASTEE, -1))
   {
      AdminReportWAStee (rqptr);
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_MEMORY, -1) ||
       strsame (pinptr, ADMIN_REPORT_MEMORY_PLUS, -1))
   {
      VmReport (rqptr);
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_MESSAGES, -1) ||
       strsame (pinptr, ADMIN_REVISE_MESSAGES, -1))
   {
      if (VMSnok (status = AdminParsePath (rqptr, NULL)))
         AdminEnd (rqptr);
      else
      if (rqptr->rqHeader.Method == HTTP_METHOD_POST)
         PutBegin (rqptr, &AdminEnd);
      else
      if (rqptr->rqHeader.QueryStringLength)
      {
         if (strsame (DoThis, "edit", -1))
            UpdBegin (rqptr, &AdminEnd);
         else
         if (strsame (pinptr, ADMIN_REPORT_MESSAGES, -1))
            MsgConfigReport (rqptr, UseServerConfig);
         else
            MsgConfigRevise (rqptr, UseServerConfig);
      }
      else
         FileBegin (rqptr, &AdminEnd, NULL, NULL,
                    rqptr->ParseOds.ExpFileName, "text/plain");
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_AUTH_PATHS, -1) ||
       strsame (pinptr, ADMIN_REVISE_AUTH_PATHS, -1))
   {
      if (VMSnok (status = AdminParsePath (rqptr, NULL)))
         AdminEnd (rqptr);
      else
      if (rqptr->rqHeader.Method == HTTP_METHOD_POST)
         PutBegin (rqptr, &AdminEnd);
      else
      if (rqptr->rqHeader.QueryStringLength)
         if (strsame (DoThis, "edit", -1))
            UpdBegin (rqptr, &AdminEnd);
         else
            AuthConfigReport (rqptr, UseServerConfig, VirtualHostPort);
      else
         FileBegin (rqptr, &AdminEnd, NULL, NULL,
                    rqptr->ParseOds.ExpFileName, "text/plain");
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_INLINE, -1) ||
       strsame (pinptr, ADMIN_REVISE_INLINE, -1))
   {
      if (VMSnok (status = AdminParsePath (rqptr, NULL)))
         AdminEnd (rqptr);
      else
      if (rqptr->rqHeader.Method == HTTP_METHOD_POST)
         PutBegin (rqptr, &AdminEnd);
      else
      if (rqptr->rqHeader.QueryStringLength)
      {
         if (strsame (DoThis, "edit", -1))
            UpdBegin (rqptr, &AdminEnd);
         else
         if (strsame (pinptr, ADMIN_REPORT_INLINE, -1))
            InConfigReport (rqptr, UseServerConfig);
         else
            InConfigReport (rqptr, UseServerConfig);
      }
      else
         FileBegin (rqptr, &AdminEnd, NULL, NULL,
                    rqptr->ParseOds.ExpFileName, "text/plain");
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_PROXY, -1))
   {
      ProxyMaintReport (rqptr);
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_PROXY_TUNNEL, -1))
   {
      ProxyTunnelReport (rqptr);
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_REQUEST, sizeof(ADMIN_REPORT_REQUEST)-1))
   {
      RequestReport (rqptr);
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_SERVICES, -1) ||
       strsame (pinptr, ADMIN_REVISE_SERVICES, -1))
   {
      if (VMSnok (status = AdminParsePath (rqptr, NULL)))
         AdminEnd (rqptr);
      else
      if (rqptr->rqHeader.Method == HTTP_METHOD_POST)
         PutBegin (rqptr, &AdminEnd);
      else
      if (rqptr->rqHeader.QueryStringLength)
      {
         if (strsame (DoThis, "edit", -1))
            UpdBegin (rqptr, &AdminEnd);
         else
         if (strsame (pinptr, ADMIN_REPORT_SERVICES, -1))
            ServiceReport (rqptr, UseServerConfig);
         else
            ServiceConfigRevise (rqptr, UseServerConfig);
      }
      else
         FileBegin (rqptr, &AdminEnd, NULL, NULL,
                    rqptr->ParseOds.ExpFileName, "text/plain");
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_SERVICE_LOG, -1))
   {
      LoggingReport (rqptr, VirtualHostPort);
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_SHOW_PROCESS, -1))
   {
      WatchShowProcess (rqptr, ProcessIdString, ProcessIdUserName);
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_STATS, -1))
   {
      AdminReportServerStats (rqptr);
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_MAPPING, -1) ||
       strsame (pinptr, ADMIN_REVISE_MAPPING, -1))
   {
      if (VMSnok (status = AdminParsePath (rqptr, NULL)))
         AdminEnd (rqptr);
      else
      if (rqptr->rqHeader.Method == HTTP_METHOD_POST)
         PutBegin (rqptr, &AdminEnd);
      else
      if (rqptr->rqHeader.QueryStringLength)
         if (strsame (DoThis, "edit", -1))
            UpdBegin (rqptr, &AdminEnd);
         else
            MapUrl_Report (rqptr, UseServerConfig, VirtualHostPort);
      else
         FileBegin (rqptr, &AdminEnd, NULL, NULL,
                    rqptr->ParseOds.ExpFileName, "text/plain");
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_SSL, -1) ||
       strsame (pinptr, ADMIN_REPORT_SSL_CLIENT, -1))
   {
      SesolaReport (rqptr, VirtualHostPort);
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_SSL_CA, -1) ||
       strsame (pinptr, ADMIN_REVISE_SSL_CA, -1))
   {
      if (VMSnok (status = AdminParsePath (rqptr, VirtualHostPort)))
         AdminEnd (rqptr);
      else
      if (rqptr->rqHeader.Method == HTTP_METHOD_POST)
         PutBegin (rqptr, &AdminEnd);
      else
      if (strsame (DoThis, "edit", -1))
         UpdBegin (rqptr, &AdminEnd);
      else
      if (strsame (DoThis, "report", -1))
         SesolaReportCA (rqptr, VirtualHostPort);
      else
         FileBegin (rqptr, &AdminEnd, NULL, NULL,
                    rqptr->ParseOds.ExpFileName, "text/plain");
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_SUPERVISOR, -1))
   {
      HttpdSupervisorReport (rqptr);
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_SYSTEM, -1))
   {
      WatchShowSystem (rqptr);
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_SYSTEM_PLUS, -1))
   {
      sysPlusReport (rqptr);
      return;
   }

   if (strsame (pinptr, ADMIN_REPORT_THROTTLE, -1))
   {
      ThrottleReport (rqptr);
      return;
   }

   if (strsame (pinptr, ADMIN_CONTROL, sizeof(ADMIN_CONTROL)-1))
   {
      /******************/
      /* server control */
      /******************/

      AdminControl (rqptr, NULL, ClusterDo);
      return;
   }

   /********************/
   /* unknown function */
   /********************/

   rqptr->rqResponse.HttpStatus = 400;
   ErrorGeneral (rqptr, "Unknown function.", FI_LI);

   AdminEnd (rqptr);
}

/*****************************************************************************/
/*
Declare the next function AST for admin processing.
*/

void AdminEnd (REQUEST_STRUCT *rqptr)

{
   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD__OTHER))
      WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER, "AdminEnd()");

   rqptr->AdminTaskPtr = NULL;

   RequestEnd (rqptr);
}

/*****************************************************************************/
/*
Compare the path info to each administration path and parses the corresponding
real file represented by that path into the request ODS parse structure so that
modules that expect this to be filled out appropriately (e.g. UPD.C and PUT.C)
can use it.  Returns a VMS status.
*/ 

int AdminParsePath
(
REQUEST_STRUCT *rqptr,
char *VirtualHostPort
)
{
   int  status;
   char  *cptr, *dnaptr, *sptr, *zptr;
   char  FileSpec [ODS_MAX_FILE_NAME_LENGTH+1];
   SERVICE_STRUCT  *svptr;

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD__OTHER))
      WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER, "AdminParsePath() !&Z !&Z",
                 rqptr->rqHeader.PathInfoPtr, VirtualHostPort);

   dnaptr = NULL;
   cptr = rqptr->rqHeader.PathInfoPtr;
   if (strsame (cptr, ADMIN_REPORT_ACCEPT, -1) ||
       strsame (cptr, ADMIN_REVISE_ACCEPT, -1))
      sptr = CONFIG_ACCEPT_FILE_NAME;
   else
   if (strsame (cptr, ADMIN_REPORT_CONFIG, -1) ||
       strsame (cptr, ADMIN_REVISE_CONFIG, -1))
      sptr = CONFIG_GLOBAL_FILE_NAME;
   else
   if (strsame (cptr, ADMIN_REPORT_INLINE, -1) ||
       strsame (cptr, ADMIN_REVISE_INLINE, -1))
      sptr = CONFIG_INLINE_FILE_NAME;
   else
   if (strsame (cptr, ADMIN_REPORT_MESSAGES, -1) ||
       strsame (cptr, ADMIN_REVISE_MESSAGES, -1))
      sptr = CONFIG_MSG_FILE_NAME;
   else
   if (strsame (cptr, ADMIN_REPORT_AUTH_PATHS, -1) ||
       strsame (cptr, ADMIN_REVISE_AUTH_PATHS, -1))
      sptr = CONFIG_AUTH_FILE_NAME;
   else
   if (strsame (cptr, ADMIN_REPORT_MAPPING, -1) ||
       strsame (cptr, ADMIN_REVISE_MAPPING, -1))
      sptr = CONFIG_MAP_FILE_NAME;
   else
   if (strsame (cptr, ADMIN_REPORT_REJECT, -1) ||
       strsame (cptr, ADMIN_REVISE_REJECT, -1))
      sptr = CONFIG_REJECT_FILE_NAME;
   else
   if (strsame (cptr, ADMIN_REPORT_SERVICES, -1) ||
       strsame (cptr, ADMIN_REVISE_SERVICES, -1))
      sptr = CONFIG_SERVICE_FILE_NAME;
   else
   if (strsame (cptr, ADMIN_REVISE_SITELOG, -1) ||
       strsame (cptr, ADMIN_REVISE_SITELOG_ENTRY, -1))
   {
      if (!(sptr = CONFIG_SITELOG_FILE_NAME))
         sptr = DEFAULT_SITELOG_FILE_NAME;
   }
   else
   if (strsame (cptr, ADMIN_REPORT_SSL_CA, -1) ||
       strsame (cptr, ADMIN_REVISE_SSL_CA, -1))
   {
      if (VirtualHostPort[0])
      {
         LIST_ITERATE (svptr, &ServiceList)
         {
            if (svptr->SchemeType != SCHEME_HTTPS) continue;
            if (strsame (svptr->ServerHostPort, VirtualHostPort, -1)) break;
         }
         if (svptr)
            sptr = ((SESOLA_CONTEXT*)svptr->SSLserverPtr)->CaFilePtr;
         else
            sptr = CONFIG_SSL_CAFILE;
      }
      else
         sptr = CONFIG_SSL_CAFILE;
   }
   else
   if (strsame (cptr, ADMIN_REVISE_HTA, sizeof(ADMIN_REVISE_HTA)-1))
   {
      /* server admin of HTA database(s) */
      zptr = (sptr = FileSpec) + sizeof(FileSpec)-1;
      if (rqptr->ConfigDirectoryLength)
         cptr = rqptr->ConfigDirectory;
      else
         cptr = AuthConfigHtaDirectory;
      while (*cptr && sptr < zptr) *sptr++ = *cptr++;
      cptr = rqptr->rqHeader.PathInfoPtr + sizeof(ADMIN_REVISE_HTA)-1;
      while (*cptr && sptr < zptr) *sptr++ = *cptr++;
      *sptr = '\0';
      sptr = FileSpec;
      dnaptr = HTA_FILE_TYPE;
   }
   else
   if (strsame (cptr, ADMIN_VS_REVISE_HTA, sizeof(ADMIN_VS_REVISE_HTA)-1))
   {
      /* per-service admin of HTA database(s) */
      if (!rqptr->ConfigDirectoryLength)
      {
         rqptr->rqResponse.HttpStatus = 403;
         ErrorGeneral (rqptr, "Service not configured to allow this.", FI_LI);
         return (STS$K_ERROR);
      }
      zptr = (sptr = FileSpec) + sizeof(FileSpec)-1;
      for (cptr = rqptr->ConfigDirectory;
           *cptr && sptr < zptr;
           *sptr++ = *cptr++);
      cptr = rqptr->rqHeader.PathInfoPtr + sizeof(ADMIN_VS_REVISE_HTA)-1;
      while (*cptr && sptr < zptr) *sptr++ = *cptr++;
      *sptr = '\0';
      sptr = FileSpec;
      dnaptr = HTA_FILE_TYPE;
   }
   else
   if (strsame (cptr, ADMIN_REVISE_HTL, sizeof(ADMIN_REVISE_HTL)-1))
   {
      /* server admin of HTL list(s) */
      zptr = (sptr = FileSpec) + sizeof(FileSpec)-1;
      if (rqptr->ConfigDirectoryLength)
         cptr = rqptr->ConfigDirectory;
      else
         cptr = AuthConfigHtaDirectory;
      while (*cptr && sptr < zptr) *sptr++ = *cptr++;
      cptr = rqptr->rqHeader.PathInfoPtr + sizeof(ADMIN_REVISE_HTL)-1;
      while (*cptr && sptr < zptr) *sptr++ = *cptr++;
      *sptr = '\0';
      sptr = FileSpec;
      dnaptr = HTL_FILE_TYPE;
   }
   else
   if (strsame (cptr, ADMIN_VS_REVISE_HTL, sizeof(ADMIN_VS_REVISE_HTL)-1))
   {
      /* per-service admin of HTL list(s) */
      if (!rqptr->ConfigDirectoryLength)
      {
         rqptr->rqResponse.HttpStatus = 403;
         ErrorGeneral (rqptr, "Service not configured to allow this.", FI_LI);
         return (STS$K_ERROR);
      }
      zptr = (sptr = FileSpec) + sizeof(FileSpec)-1;
      for (cptr = rqptr->ConfigDirectory;
           *cptr && sptr < zptr;
           *sptr++ = *cptr++);
      cptr = rqptr->rqHeader.PathInfoPtr + sizeof(ADMIN_VS_REVISE_HTL)-1;
      while (*cptr && sptr < zptr) *sptr++ = *cptr++;
      *sptr = '\0';
      sptr = FileSpec;
      dnaptr = HTL_FILE_TYPE;
   }
   else
      return (SS$_NORMAL);

   OdsStructInit (&rqptr->ParseOds, true);

   status = OdsParse (&rqptr->ParseOds, sptr, strlen(sptr),
                      dnaptr, dnaptr ? strlen(dnaptr) : 0,
                      NAM$M_SYNCHK, NULL, rqptr);

   if (VMSok(status)) status = rqptr->ParseOds.Fab.fab$l_sts;
   if (VMSok(status)) status = OdsParseTerminate (&rqptr->ParseOds);

   if (VMSok(status))
   {
      if (WATCHING (rqptr, WATCH_RESPONSE))
         WatchThis (WATCHITM(rqptr), WATCH_RESPONSE,
                    "FILE !AZ !AZ", sptr, rqptr->ParseOds.ExpFileName);
      return (status);
   }

   rqptr->rqResponse.ErrorTextPtr = cptr;
   rqptr->rqResponse.ErrorOtherTextPtr = sptr;
   ErrorVmsStatus (rqptr, status, FI_LI);
   return (status);
}

/*****************************************************************************/
/*
Execute server administration menu control actions.
*/ 

void AdminControl
(
REQUEST_STRUCT *rqptr,
char *DoThis,
BOOL ClusterDo
)
{
   int  cnt, status,
        LockIndex;
   char  *cptr, *sptr, *zptr;
   char  String [256];

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD__OTHER))
      WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER,
                 "AdminControl() !BZ !&B !&Z",
                 DoThis, ClusterDo, rqptr->rqHeader.PathInfoPtr);

   cptr = rqptr->rqHeader.PathInfoPtr;

   sptr = NULL;
   if (DoThis)
      sptr = DoThis;
   else
   if (strsame (cptr, ADMIN_CONTROL_AUTH_LOAD, -1))
      sptr = CONTROL_AUTH_LOAD1;
   else
   if (strsame (cptr, ADMIN_CONTROL_AUTH_PURGE, -1))
      sptr = CONTROL_AUTH_PURGE;
   else
   if (strsame (cptr, ADMIN_VS_CONTROL_AUTH_PURGE, -1))
      sptr = CONTROL_AUTH_PURGE;
   else
   if (strsame (cptr, ADMIN_CONTROL_CACHE_ON, -1))
      sptr = CONTROL_CACHE_ON;
   else
   if (strsame (cptr, ADMIN_CONTROL_CACHE_OFF, -1))
      sptr = CONTROL_CACHE_OFF;
   else
   if (strsame (cptr, ADMIN_CONTROL_CACHE_PURGE, -1))
      sptr = CONTROL_CACHE_PURGE;
   else
   if (strsame (cptr, ADMIN_CONTROL_DCL_PURGE, -1))
      sptr = CONTROL_DCL_PURGE;
   else
   if (strsame (cptr, ADMIN_CONTROL_DCL_DELETE, -1))
      sptr = CONTROL_DCL_DELETE;
   else
   if (strsame (cptr, ADMIN_CONTROL_EXIT, -1))
      sptr = CONTROL_EXIT;
   else
   if (strsame (cptr, ADMIN_CONTROL_EXIT_NOW, -1))
      sptr = CONTROL_EXIT_NOW;
   else
   if (strsame (cptr, ADMIN_CONTROL_HTTP2_PURGE, -1))
      sptr = CONTROL_HTTP2_PURGE;
   else
   if (strsame (cptr, ADMIN_CONTROL_HTTP2_PURGE_ALL, -1))
      sptr = CONTROL_HTTP2_PURGE_ALL;
   else
   if (strsame (cptr, ADMIN_CONTROL_INSTANCE_ACTIVE, -1))
      sptr = CONTROL_INSTANCE_ACTIVE;
   else
   if (strsame (cptr, ADMIN_CONTROL_INSTANCE_PASSIVE, -1))
      sptr = CONTROL_INSTANCE_PASSIVE;
   else
   if (strsame (cptr, ADMIN_CONTROL_INSTANCE,
                sizeof(ADMIN_CONTROL_INSTANCE)-1))
   {
      sptr = cptr;
      while (*cptr) cptr++;
      while (cptr > sptr && *cptr != '/') cptr--;
      if (*cptr == '/')
      {
         cptr++;
         FaoToBuffer (String, sizeof(String), NULL, "!AZ!AZ",
                      CONTROL_INSTANCE, cptr);
         sptr = String;
      }
   }
   else
   if (strsame (cptr, ADMIN_CONTROL_LOG_OPEN, -1))
      sptr = CONTROL_LOG_OPEN;
   else
   if (strsame (cptr, ADMIN_CONTROL_LOG_CLOSE, -1))
      sptr = CONTROL_LOG_CLOSE;
   else
   if (strsame (cptr, ADMIN_CONTROL_LOG_FLUSH, -1))
      sptr = CONTROL_LOG_FLUSH;
   else
   if (strsame (cptr, ADMIN_CONTROL_MAP, -1))
      sptr = CONTROL_MAP_LOAD1;
   else
   if (strsame (cptr, ADMIN_CONTROL_NET_ACCEPT_REJECT, -1))
      sptr = CONTROL_ACCEPT_REJECT;
   else
   if (strsame (cptr, ADMIN_CONTROL_NET_PURGE, -1))
      sptr = CONTROL_NET_PURGE;
   else
   if (strsame (cptr, ADMIN_CONTROL_NET_PURGE_ALL, -1))
      sptr = CONTROL_NET_PURGE_ALL;
   else
   if (strsame (cptr, ADMIN_CONTROL_NET_PURGE_HTTP1, -1))
      sptr = CONTROL_NET_PURGE_HTTP1;
   else
   if (strsame (cptr, ADMIN_CONTROL_NET_PURGE_HTTP2, -1))
      sptr = CONTROL_NET_PURGE_HTTP2;
   else
   if (strsame (cptr, ADMIN_CONTROL_NET_PURGE_REJECT, -1))
      sptr = CONTROL_REJECT_PURGE;
   else
   if (strsame (cptr, ADMIN_CONTROL_RESTART, -1))
      sptr = CONTROL_RESTART;
   else
   if (strsame (cptr, ADMIN_CONTROL_RESTART_NOW, -1))
      sptr = CONTROL_RESTART_NOW;
   else
   if (strsame (cptr, ADMIN_CONTROL_RESTART_QUIET, -1))
      sptr = CONTROL_RESTART_QUIET;
   else
   if (strsame (cptr, ADMIN_CONTROL_SSL_CA_LOAD, -1))
      sptr = CONTROL_SSL_CA_LOAD;
   else
   if (strsame (cptr, ADMIN_CONTROL_THROTTLE_RELEASE, -1))
      sptr = CONTROL_THROTTLE_RELEASE;
   else
   if (strsame (cptr, ADMIN_CONTROL_THROTTLE_TERMINATE, -1))
      sptr = CONTROL_THROTTLE_TERMINATE;
   else
   if (strsame (cptr, ADMIN_CONTROL_WEBSOCKET_DISCONNECT, -1))
      sptr = CONTROL_WEBSOCKET_DISCONNECT;
   else
   if (strsame (cptr, ADMIN_CONTROL_THROTTLE_ZERO, -1))
      sptr = CONTROL_THROTTLE_ZERO;
   else
   if (strsame (cptr, ADMIN_CONTROL_WATCH_END, -1))
      sptr = CONTROL_WATCH_END;
   else
   if (strsame (cptr, ADMIN_CONTROL_ZERO_NOTICED, -1))
      sptr = CONTROL_ZERO_NOTICED;
   else
   if (strsame (cptr, ADMIN_CONTROL_ZERO, -1))
      sptr = CONTROL_ZERO;

   if (!sptr)
   {
      /* unknown function */
      rqptr->rqResponse.HttpStatus = 400;
      ErrorGeneral (rqptr, "Unknown control function.", FI_LI);
      AdminEnd (rqptr);
      return;
   }

   if (ClusterDo)
      LockIndex = INSTANCE_CLUSTER_DO;
   else
      LockIndex = INSTANCE_NODE_DO;

   cptr = ControlEnqueueCommand (LockIndex, sptr);

   strzcat (cptr, ".", ControlStringSize);
   if (*cptr == '!')
   {
      rqptr->rqResponse.HttpStatus = 403;
      cptr[1] = to_upper(cptr[1]);
      ErrorGeneral (rqptr, cptr+1, FI_LI);
   }
   else
   {
      cptr[0] = to_upper(cptr[0]);
      ReportSuccess (rqptr, "Command !AZ", cptr);
   }

   AdminEnd (rqptr);
}

/*****************************************************************************/
/*
Provide a menu of HTTPd server administration functions.
*/ 

void AdminMenu (REQUEST_STRUCT *rqptr)

{
   static int ActHours [] = { 0,1,2,4,8,16,24,72,168,336,504,672,0 };

   static char  BeginPageFao [] =
"!AZ\
<html>\n\
<head>\n\
!AZ\
!AZ\
!&@\
<title>WASD !AZ ... Server Administration</title>\n\
\
<script nonce=\"!AZ\">\n\
\
function doIt(link,msg1,msg2) {\n\
!&@\
   if (typeof msg1 == 'undefined') return true;\n\
   return confirm (msg1 + ' ' + scope + ' ' + msg2);\n\
}\n\
\
const capture = { capture : true };\n\
\
function setClicks() {\n\
\
   if (!!document.getElementById('ender')) {\n\
      setTimeout(setClicks,50);\n\
      return;\n\
   }\n\
\
   var clk = document.getElementById('doRestart');\n\
   clk.addEventListener(\"click\",doItRestart);\n\
   clk = document.getElementById('doRestartNow');\n\
   clk.addEventListener(\"click\",doItRestartNow);\n\
   clk = document.getElementById('doRestartQuiet');\n\
   clk.addEventListener(\"click\",doItRestartQuiet);\n\
   clk = document.getElementById('doExit');\n\
   clk.addEventListener(\"click\",doItExit);\n\
   clk = document.getElementById('doExitNow');\n\
   clk.addEventListener(\"click\",doItExitNow);\n\
\
   clk = document.getElementById('doZero');\n\
   clk.addEventListener(\"click\",doItZero);\n\
   clk = document.getElementById('doNetPurge');\n\
   clk.addEventListener(\"click\",doItNetPurge);\n\
   clk = document.getElementById('doNetPurgeAll');\n\
   clk.addEventListener(\"click\",doItNetPurgeAll);\n\
\
   clk = document.getElementById('doZeroProxy');\n\
   if (clk) clk.addEventListener(\"click\",doItZeroProxy);\n\
   clk = document.getElementById('doActive');\n\
   if (clk) clk.addEventListener(\"click\",doItActive);\n\
   clk = document.getElementById('doPassive');\n\
   if (clk) clk.addEventListener(\"click\",doItPasive);\n\
   clk = document.getElementById('doLogOn');\n\
   if (clk) clk.addEventListener(\"click\",doItRegardless);\n\
   clk = document.getElementById('doLogOff');\n\
   if (clk) clk.addEventListener(\"click\",doItRegardless);\n\
   clk = document.getElementById('doLogFlush');\n\
   if (clk) clk.addEventListener(\"click\",doItRegardless);\n\
   clk = document.getElementById('doCacheOn');\n\
   if (clk) clk.addEventListener(\"click\",doItRegardless);\n\
   clk = document.getElementById('doCacheOff');\n\
   if (clk) clk.addEventListener(\"click\",doItRegardless);\n\
   clk = document.getElementById('doCacheFlush');\n\
   if (clk) clk.addEventListener(\"click\",doItRegardless);\n\
\
   clk = document.getElementById('doInstMax');\n\
   if (clk) clk.addEventListener(\"click\",doItRegardless);\n\
   clk = document.getElementById('doInstCPU');\n\
   if (clk) clk.addEventListener(\"click\",doItRegardless);\n\
   for (var cnt = 1; cnt <= !UL; cnt++) {\n\
      clk = document.getElementById('doInst'+cnt);\n\
      if (clk) clk.addEventListener(\"click\",doItRegardless);\n\
   }\n\
\
   clk = document.getElementById('doMapReload');\n\
   clk.addEventListener(\"click\",doItRegardless);\n\
   clk = document.getElementById('doAuthReload');\n\
   clk.addEventListener(\"click\",doItRegardless);\n\
   clk = document.getElementById('doAuthPurge');\n\
   clk.addEventListener(\"click\",doItRegardless);\n\
   clk = document.getElementById('doLoadCA');\n\
   clk.addEventListener(\"click\",doItRegardless);\n\
\
}\n\
\
function doItRestart(event) {\n\
   if (!!doIt(this,'Restart','after allowing current requests to complete?')) \
event.preventDefault();\n\
}\n\
function doItRestartNow(event) {\n\
   if (!!doIt(this,'Restart','DISCONNECTING current requests?')) \
event.preventDefault();\n\
}\n\
function doItRestartQuiet(event) {\n\
   if (!!doIt(this,'Restart','when current requests reach zero?')) \
event.preventDefault();\n\
}\n\
function doItExit(event) {\n\
   if (!!doIt(this,'Exit','allowing current requests to complete?')) \
event.preventDefault();\n\
}\n\
function doItExitNow(event) {\n\
   if (!!doIt(this,'Exit','DISCONNECTING current requests?')) \
event.preventDefault();\n\
}\n\
\
function doItZeroProxy(event) {\n\
   if (!!doIt(this,'Zero','proxy accounting?')) \
event.preventDefault();\n\
}\n\
function doItActive(event) {\n\
   if (!!doIt(this,'Allow','network connections to just all instances \
(active mode)?')) \
event.preventDefault();\n\
}\n\
function doItPassive(event) {\n\
   if (!!doIt(this,'Limit','network connections to just one instance \
(passive mode)?')) \
event.preventDefault();\n\
}\n\
\
function doItZero(event) {\n\
   if (!!doIt(this,'Zero','server accounting?')) \
event.preventDefault();\n\
}\n\
function doItNetPurge() {\n\
   if (!!doIt(this,'Purge','persistent (idle) network connections?')) \
event.preventDefault();\n\
}\n\
function doItNetPurgeAll() {\n\
   if (!!doIt(this,'Purge','all (idle and in-progress) \
network connections, DISCONNECTING current requests?')) \
event.preventDefault();\n\
}\n\
\
function doItRegardless() {\n\
   return doIt(this);\n\
}\n\
\
!AZ\
\
// Safari (at least) does not honour HTTP cache control headers!!\n\
// Its back-forward cache mechanism breaks stuff.  These are kludges!!\n\
var dueToSafariFBCache = new Date().getTime();\n\
\
window.addEventListener(\"pageshow\",function(event) {\n\
   if (event.persisted) window.location.reload()\n\
});\n\
\
window.addEventListener(\"focus\",function(event) {\n\
  if (new Date().getTime() - dueToSafariFBCache > 60000) \
window.location.reload()\n\
});\n\
\
setClicks()\n\
\
</script>\n\
\
<style type=\"text/css\">\n\
table.admn1 { margin:0.4em; border-style:none; border-collapse:collapse; \
text-align:center; }\n\
table.admn1 th, table.admn1 td { padding:0.1em 0.1em 0.1em 0.1em; }\n\
input { border-style:groove; border-width:2px; \
font-family:monospace; font-size:90%; }\n\
table.admn2 { border-style:none; border-collapse:collapse; \
margin:0.2em 0.2em 0.6em 0.2em; font-size:80%; }\n\
table.admn2 th { padding:0.1em 0.2em 0.1em 0.2em; text-align:right; \
vertical-align:top; }\n\
table.admn2 td { padding:0.1em 0.2em 0.1em 0.2em; text-align:left; \
vertical-align:top; }\n\
</style>\n\
\
</head>\n\
<body!AZ\n\
<div class=\"wasd\">\n\
<h2><nobr>WASD !AZ</nobr></h2>\n\
<h3>Server Administration!&@</h3>\n\
<p>\n\
\
<form name=\"doallform\">\n\
\
<table class=\"ctgry\">\n\
\
<tr><th class=\"ctttl\" style=\"height:1.2em!!important;\
vertical-align:middle;\" colspan=\"2\">Configuration</th><tr>\n\
\
<tr><td class=\"tacnt\" colspan=\"2\">\n\
\
<table class=\"admn1\" style=\"margin-left:1em;\">\n\
\
<tr><td></td><td>|</td>\
<th colspan=\"2\">Report</th><td>|</td>\n\
<th colspan=\"3\">Revise</th><td>|</td>\n\
<th colspan=\"2\">Action</th><td>|</td><tr>\n\
\
<tr>\n\
<th>Server</th>\n\
<td></td>\n\
<td><a class=\"abttn bttn400\" href=\"!AZ\">Statistics</a></td>\n\
<td>!&@</td>\n\
<td></td>\n\
<td colspan=\"2\"><a class=\"abttn bttn400\" \
href=\"!AZ?do=edit\">Site-Log</a></td>\n\
<td><a class=\"abttn bttn300\" href=\"!AZ?do=edit\">Edit</a></td>\n\
<td></td>\n\
<td><a id=\"doZero\" class=\"abttn bttn300\" href=\"!AZ\">Zero</a></td>\n\
</tr>\n\
\
!&@\
\
<tr>\n\
<th>Global</th>\n\
<td></td>\n\
<td><a class=\"abttn bttn400\" href=\"!AZ?server=yes\">Server</a></td>\n\
<td><a class=\"abttn bttn300\" href=\"!AZ?file=yes\">File</a></td>\n\
<td></td>\n\
<td><a class=\"abttn bttn400\" href=\"!AZ?server=yes\">Server</a></td>\n\
<td><a class=\"abttn bttn300\" href=\"!AZ?file=yes\">File</a></td>\n\
<td><a class=\"abttn bttn300\" href=\"!AZ?do=edit\">Edit</a></td>\n\
</tr>\n\
\
<tr>\n\
<th>Services</th>\n\
<td></td>\n\
<td><a class=\"abttn bttn400\" href=\"!AZ?server=yes\">Server</a></td>\n\
<td><a class=\"abttn bttn300\" href=\"!AZ?file=yes\">File</a></td>\n\
<td></td>\n\
<td><a class=\"abttn bttn400\" href=\"!AZ?server=yes\">Server</a></td>\n\
<td><a class=\"abttn bttn300\" href=\"!AZ?file=yes\">File</a></td>\n\
<td><a class=\"abttn bttn300\" href=\"!AZ?do=edit\">Edit</a></td>\n\
<td></td>\n\
<td><a id=\"doNetPurge\" class=\"abttn bttn300\" href=\"!AZ\">Purge</a></td>\n\
<td><a id=\"doNetPurgeAll\" class=\"abttn bttn200\" href=\"!AZ\">All</a></td>\n\
</tr>\n\
\
<tr>\n\
<th>Messages</th>\n\
<td></td>\n\
<td><a class=\"abttn bttn400\" href=\"!AZ?server=yes\">Server</a></td>\n\
<td><a class=\"abttn bttn300\" href=\"!AZ?file=yes\">File</a></td>\n\
<td></td>\n\
<td><a class=\"abttn bttn400\" href=\"!AZ?server=yes\">Server</a></td>\n\
<td><a class=\"abttn bttn300\" href=\"!AZ?file=yes\">File</a></td>\n\
<td><a class=\"abttn bttn300\" href=\"!AZ?do=edit\">Edit</a></td>\n\
</tr>\n\
\
<tr>\n\
<th>Path Mapping</th>\n\
<td></td>\n\
<td><a class=\"abttn bttn400\" href=\"!AZ?server=yes\">Server</a></td>\n\
<td><a class=\"abttn bttn300\" href=\"!AZ?file=yes\">File</a></td>\n\
<td></td>\n\
<td></td>\n\
<td></td>\n\
<td><a class=\"abttn bttn300\" href=\"!AZ?do=edit\">Edit</a></td>\n\
<td></td>\n\
<td colspan=\"2\"><a id=\"doMapReload\" class=\"abttn bttn400\" href=\"!AZ\">\
Reload</a></td>\n\
</tr>\n\
\
<tr>\n\
<th>Path Authorization</th>\n\
<td></td>\n\
<td><a class=\"abttn bttn400\" href=\"!AZ?server=yes\">Server</a></td>\n\
<td><a class=\"abttn bttn300\" href=\"!AZ?file=yes\">File</a></td>\n\
<td></td>\n\
<td></td>\n\
<td></td>\n\
<td><a class=\"abttn bttn300\" href=\"!AZ?do=edit\">Edit</a></td>\n\
<td></td>\n\
<td colspan=\"2\"><a id=\"doAuthReload\" class=\"abttn bttn400\" href=\"!AZ\">\
Reload</a></td>\n\
</tr>\n\
\
<tr>\n\
<th>User Authentication</th>\n\
<td></td>\n\
<td><a class=\"abttn bttn400\" href=\"!AZ\">Server</a></td>\n\
<td></td>\n\
<td></td>\n\
<td colspan=\"2\"><a class=\"abttn bttn300\" href=\"!AZ\">HTA</a>\
<a class=\"abttn bttn300\" href=\"!AZ\">HTL</a></td>\n\
<td></td>\n\
<td></td>\n\
<td colspan=\"2\"><a id=\"doAuthPurge\" class=\"abttn bttn400\" href=\"!AZ\">\
Purge</a></td>\n\
</tr>\n\
\
<tr>\n\
<th>Access Control</th>\n\
<td></td>\n\
<td><a class=\"abttn bttn400!AZ\"!AZ?do=accept\">Accept</a></td>\n\
<td><a class=\"abttn bttn420!AZ\"!AZ?do=reject\">Reject</a></td>\n\
<td></td>\n\
<td><a class=\"abttn bttn400!AZ\"!AZ?do=edit\">Accept</a></td>\n\
<td colspan=\"2\"><a class=\"abttn bttn400!AZ\"!AZ?do=edit\">Reject</a></td>\n\
<td></td>\n\
<td><a class=\"abttn bttn400!AZ\"!AZ\">Reload</a></td>\n\
<td><a class=\"abttn bttn300!AZ\"!AZ\">Purge</a></td>\n\
</tr>\n\
\
<tr>\n\
<th>Secure Sockets</th>\n\
<td></td>\n\
<td>!&@</td>\n\
<td>!&@</td>\n\
<td></td>\n\
<td></td>\n\
<td class=\"targht\" colspan=\"2\">!&@</td>\n\
<td></td>\n\
<td colspan=\"2\">!&@</td>\n\
</tr>\n\
\
<tr>\n\
<th style=\"padding-top:0.5em;\" rowspan=\"4\">Other Reports</th>\n\
<td rowspan=\"4\"></td>\n\
<td colspan=\"11\" style=\"padding-top:0.5em;\">\n\
!&@\n\
!&@\n\
!&@\n\
!&@\n\
!&@\n\
!&@\n\
<td></td></tr>\n\
<tr><td colspan=\"11\">\n\
!&@\n\
!&@\n\
!&@\n\
!&@\n\
!&@\n\
!&@\n\
<td></td></tr>\n\
<tr><td colspan=\"11\">\n\
!&@\n\
!&@\n\
!&@\n\
!&@\n\
!&@\n\
!&@\n\
</td></tr>\n\
<tr><td colspan=\"11\" style=\"padding-bottom:0.4em;\">\n\
!&@!&@!&@!&@!&@!&@!&@!&@!&@!&@!&@ \
<span style=\"font-size:80%\">hours activity</span>\n\
</td></tr>\n\
\
</table>\n\
\
<tr>\n\
<th class=\"ctttl\" style=\"height:1.2em!!important;\
vertical-align:middle;\">Control</th>\n\
\
<td class=\"ctttl\" rowspan=\"2\" style=\"text-align:center;\">\n\
<!!-- 16-DEC-2017 if Chrome shows a non-collapsed border around here \
it's due to a decade-old bug in it!!  Fixed from version 68...  -->\n\
\
<table class=\"admn2\">\n\
<tr><th colspan=\"4\" style=\"text-align:center;font-size:105%;\">\
!20&W</th></tr>\n\
<tr><td colspan=\"4\" style=\"padding:0.2em;text-align:center;\
font-size:80%;\">!AZ (!AZ)</td></tr>\n\
\
<tr><td colspan=\"3\" lang=\"en\" \
style=\"width:5em!!important;padding:0.1em 0 0.3em 0;white-space:normal;\
hyphens:auto;text-align:center;\">!&@</td></tr>\n\
\
<tr><th class=\"talft\">Time</th>\n\
<th>!AZ::</th>\
<td>!%D</td></tr>\n\
<tr><th colspan=\"2\">!AZ:</th>\
<td>!%D</td></tr>\n\
<tr><th colspan=\"2\">Image:</th>\
<td>!%D</td></tr>\n\
<tr><th colspan=\"2\">CPU:</th>\
<td>!UL !2ZL:!2ZL:!2ZL.!2ZL</td></tr>\n\
\
<tr><th class=\"talft\" rowspan=\"3\">Connect</th>\n\
<th>Current:</th>\
<td>!&L</td></tr>\n\
<tr><th>Peak:</th>\
<td>!&L</td></tr>\n\
<tr><th>Total:</th>\
<td>!&L</td></tr>\n\
\
<tr><th class=\"talft\" rowspan=\"4\">Request</th>\n\
<th>Current:</th>\
<td>!&L</td></tr>\n\
<tr><th>Peak:</th>\
<td>!&L</td></tr>\n\
<tr><th>Total:</th>\
<td>!&L</td></tr>\n\
<tr><th>Bytes:</th>\
<td>!&,@UQ</td></tr>\n\
\
!&@\
!&@\
!AZ\
!&@\
!AZ\
!AZ\
\
</table>\n\
\
</td></tr>\n\
<tr><td>\n\
\
<table class=\"admn1\">\n\
<tr><td>\n\
\
<table class=\"admn1\">\n\
<tr><td>\
<a id=\"doRestart\" class=\"abttn bttn800\" href=\"!AZ\">RESTART</a>\
</td></tr>\n\
<tr><td>\
<a id=\"doRestartNow\" class=\"abttn bttn800\" href=\"!AZ\">\
<span style=\"font-size:85%;\">RESTART</span>NOW\
</a>\
</td></tr>\n\
<tr><td>\
<a id=\"doRestartQuiet\" class=\"abttn bttn800\" href=\"!AZ\">\
<span style=\"font-size:85%;\">RESTART</span>Quiet\
</a>\
</td></tr>\n\
<tr><td>\
<a id=\"doExit\" class=\"abttn bttn800\" href=\"!AZ\">EXIT</a>\
</td></tr>\n\
<tr><td>\
<a id=\"doExitNow\" class=\"abttn bttn800\" href=\"!AZ\">\
<span style=\"font-size:85%;\">EXIT</span>NOW\
</a>\
</td></tr>\n\
</table>\n\
\
</td><td>\n\
\
<table class=\"admn1\">\n\
<tr><td style=\"text-align:right;padding-right:0.4em;\"><b>Log</b></td><td>!&@</td></tr>\n\
<tr><td style=\"text-align:right;padding-right:0.4em;\"><b>Cache</b></td><td>!&@</td></tr>\n\
<tr><td style=\"text-align:right;padding-right:0.4em;\"><b>Instance</b></td><td>\
!&@\
!&@";

   static char  EndInstanceFao [] =
"</td></tr>\n\
<tr><td></td><td>!&@</td></tr>\n\
</table>\n\
\
</td></tr>\n\
<tr><td colspan=\"3\" style=\"padding:0 0.5em 0.5em 0.5em;text-align:right;\">\
<input type=\"submit\" style=\"min-width:5em;\" value=\"/DO=\">\
<input name=\"slashdo\" type=\"text\" size=\"25\" \
style=\"max-width:25em;border:1px solid gray;outline:none;\">\
</td></tr>\n\
</table>\n\
</td></tr>\n\
<!!-- 16-DEC-2017 if Chrome shows a non-collapsed border around here \
it's due to a decade-old bug in it!! -->\n";

   static char  EndPageFao [] =
"</table>\n\
\
</form>\n\
\
<script nonce=\"!AZ\">var norefresh = true;</script>\n\
!AZ\
\
</div>\n\
<span id=\"ender\"></span>\n\
</body>\n\
</html>\n";

   static unsigned long  LibDeltaHours = LIB$K_DELTA_HOURS;

   static unsigned short  SyiClusterNodes;
   static unsigned long  JpiCpuTime,
                         Pid = 0;
   static int64  ActivityUpTime64,
                 Time64,
                 ImageUpTime64,
                 JpiLoginTime64,
                 ProcessUpTime64,
                 SystemUpTime64;
   static VMS_ITEM_LIST3  JpiItems [] =
   {
      { sizeof(JpiCpuTime), JPI$_CPUTIM, &JpiCpuTime, 0 },
      { sizeof(JpiLoginTime64), JPI$_LOGINTIM, &JpiLoginTime64, 0 },
      {0,0,0,0}
   };
   static VMS_ITEM_LIST3  SyiItem [] =
   {
     { sizeof(SyiClusterNodes), SYI$_CLUSTER_NODES, &SyiClusterNodes, 0 },
     { 0,0,0,0 }
   };

   int  cnt, idx, status, total,
        ClusterInstanceCount,
        InstanceStartupMax,
        NodeInstanceCount;
   int64  BytesTotal64;
   unsigned short  Length;
   unsigned long  ActivityDeltaHours;
   unsigned long  *vecptr;
   unsigned long  FaoVector [192];
   char  *accptr, *cptr, *rejptr,
         *ClusterInstancePtr,
         *NodeInstancePtr;
   IO_SB  IOsb;

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD__OTHER))
      WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER, "AdminMenu()");

   if (HTTP2_REQUEST(rqptr))
      Http2Ping (rqptr->Http2Ptr, 0, 0, NULL, 0);

   status = sys$getjpiw (EfnWait, &Pid, 0, &JpiItems, &IOsb, 0, 0);
   if (VMSok (status)) status = IOsb.Status;
   if (VMSnok (status))
   {
      rqptr->rqResponse.ErrorTextPtr = "sys$getjpiw()";
      ErrorVmsStatus (rqptr, status, FI_LI);
      AdminEnd (rqptr);
      return;
   }

   status = sys$getsyiw (EfnWait, 0, 0, &SyiItem, &IOsb, 0, 0);
   if (VMSok (status)) status = IOsb.Status;
   if (VMSnok (status))
   {
      rqptr->rqResponse.ErrorTextPtr = "sys$getsyiw()";
      ErrorVmsStatus (rqptr, status, FI_LI);
      AdminEnd (rqptr);
      return;
   }
#ifdef WATCH_MOD
   if (!SyiClusterNodes)
      if (cptr = SysTrnLnm (WASD_CLUSTER_NODES))
         SyiClusterNodes = atoi(cptr);
#endif

   sys$gettim (&Time64);
   SystemUpTime64 = SysInfo.BootTime64 - Time64;
   ImageUpTime64 = HttpdStartTime64 - Time64;
   ProcessUpTime64 = JpiLoginTime64 - Time64;

   if (ActivityTotalMinutes)
   {
      InstanceMutexLock (INSTANCE_MUTEX_HTTPD);
      ActivityUpTime64 = ActivityGblSecPtr->StartTime64 - Time64;
      InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);
      ActivityDeltaHours = -ActivityUpTime64 / TIME64_ONE_HOUR;
      if (!ActivityDeltaHours) ActivityDeltaHours = 1;
   }
   else
      ActivityDeltaHours = 1;

   /* get the number (if any) of other HTTP servers on this node/cluster */
   NodeInstanceCount = InstanceLockList (INSTANCE_NODE_SUPERVISOR, "\n",
                                         &NodeInstancePtr);
   ClusterInstanceCount = InstanceLockList (INSTANCE_CLUSTER, "\n",
                                            &ClusterInstancePtr);

   vecptr = FaoVector;

   *vecptr++ = WASD_DOCTYPE;
   *vecptr++ = HtmlMetaInfo (rqptr, NULL);
   *vecptr++ = AdminWasdCss ();
   if (rqptr->rqPathSet.StyleSheetPtr)
   {
      *vecptr++ = "<link rel=\"stylesheet\" type=\"text/css\" href=\"!AZ\">\n";
      *vecptr++ = rqptr->rqPathSet.StyleSheetPtr;
   }
   else
      *vecptr++ = "";
   *vecptr++ = ServerHostPort;
   *vecptr++ = GenerateCspNonce (rqptr);

   if (SyiClusterNodes > 1 && ClusterInstanceCount > NodeInstanceCount)
   {
      *vecptr++ =
"   scope = 'node instance!&?s\r\r';\n\
   if (document.doallForm.scope[1].checked) {\n\
      if (link.href.indexOf('?') >= 0) \
link.href = link.href.substring(0,link.href.indexOf('?'));\n\
      link.href = link.href + '?scope=cluster';\n\
      scope = 'cluster instances';\n\
   }\n";
      *vecptr++ = (NodeInstanceCount > 1);
   }
   else
   {
      *vecptr++ = "   scope = 'node instance!&?s\r\r';\n";
      *vecptr++ = (NodeInstanceCount > 1);
   }
   *vecptr++ = INSTANCE_MAX;

   *vecptr++ = AdminPing (NULL);

   *vecptr++ = ADMIN_BODY_TAG + 5;
   *vecptr++ = ServerHostPort;
   if (InstanceNodeCurrent > 1)
   {
      if (rqptr->ServicePtr->AdminService)
         *vecptr++ = " &nbsp;-&nbsp; !AZ";
      else
         *vecptr++ = " &nbsp;&nbsp; (!AZ)";
      *vecptr++ = HttpdProcess.PrcNam;
   }
   else
      *vecptr++ = "";

   /* statistics */
   *vecptr++ = ADMIN_REPORT_STATS;

   if (HttpdProcess.SysOutputFile)
   {
      *vecptr++ = "<span class=\"abttn bttn400\">\
<a class=\"outa\" href=\"!AZ\">Log</a>\
<a class=\"outa outu\" href=\"!AZ\">&#8711;</a>\
<a class=\"outa outu\" href=\"!AZ\">&#991;</a>\
<a class=\"outa outu\" !AZ\"!AZ\">&#8286;&#8286;</a>\
</span>";
      *vecptr++ = ADMIN_REPORT_PROCESS_OUTPUT_RAW;
      *vecptr++ = ADMIN_REPORT_PROCESS_OUTPUT_DELTA;
      *vecptr++ = ADMIN_REPORT_PROCESS_OUTPUT_NOTICED;
      *vecptr++ = rqptr->rqAuth.VmsUserProfileLength ? "href=" : "";
      *vecptr++ = ADMIN_REPORT_PROCESS_OUTPUT_LIST;
   }
   else
      *vecptr++ = "<a class=\"abttn bttn400 dbttn\">Log</a>";

   *vecptr++ = ADMIN_REVISE_SITELOG_ENTRY;
   *vecptr++ = ADMIN_REVISE_SITELOG;
   *vecptr++ = ADMIN_CONTROL_ZERO;

   /* inline config */
   if (SysTrnLnm (CONFIG_INLINE_FILE_NAME) ||
       SysTrnLnm (CONFIG_INLINE_REPORT_FILE_NAME))
   {
      *vecptr++ =
"<tr>\n\
<th style=\"font-size:107%;\">Inline</th>\n\
<td></td>\n\
<td><a class=\"abttn bttn400!AZ\" !AZ\"!AZ?server=yes\">Server</a></td>\n\
<td><a class=\"abttn bttn300!AZ\" !AZ\"!AZ?file=yes\">File</a></td>\n\
<td></td>\n\
<td></td>\n\
<td></td>\n\
<td><a class=\"abttn bttn300!AZ\" !AZ\"!AZ?do=edit\">Edit</a></td>\n\
<td></td>\n\
</tr>\n";
      *vecptr++ = InConfigFileName[0] ? "" : " dbttn";
      *vecptr++ = InConfigFileName[0] ? "href=" : "";
      *vecptr++ = ADMIN_REPORT_INLINE;
      if (SysTrnLnm (CONFIG_INLINE_REPORT_FILE_NAME))
      {
         *vecptr++ = "";
         *vecptr++ = "href=";
      }
      else
      {
         *vecptr++ = InConfigFileName[0] ? "" : " dbttn";
         *vecptr++ = InConfigFileName[0] ? "href=" : "";
      }
      *vecptr++ = ADMIN_REPORT_INLINE;
      *vecptr++ = InConfigFileName[0] ? "" : " dbttn";
      *vecptr++ = InConfigFileName[0] ? "href=" : "";
      *vecptr++ = ADMIN_REVISE_INLINE;
   }
   else
      *vecptr++ = "";

   /* global */
   *vecptr++ = ADMIN_REPORT_CONFIG;
   *vecptr++ = ADMIN_REPORT_CONFIG;
   *vecptr++ = ADMIN_REVISE_CONFIG;
   *vecptr++ = ADMIN_REVISE_CONFIG;
   *vecptr++ = ADMIN_REVISE_CONFIG;

   /* services */
   *vecptr++ = ADMIN_REPORT_SERVICES;
   *vecptr++ = ADMIN_REPORT_SERVICES;
   *vecptr++ = ADMIN_REVISE_SERVICES;
   *vecptr++ = ADMIN_REVISE_SERVICES;
   *vecptr++ = ADMIN_REVISE_SERVICES;
   *vecptr++ = ADMIN_CONTROL_NET_PURGE;
   *vecptr++ = ADMIN_CONTROL_NET_PURGE_ALL;

   /* messages */
   *vecptr++ = ADMIN_REPORT_MESSAGES;
   *vecptr++ = ADMIN_REPORT_MESSAGES;
   *vecptr++ = ADMIN_REVISE_MESSAGES;
   *vecptr++ = ADMIN_REVISE_MESSAGES;
   *vecptr++ = ADMIN_REVISE_MESSAGES;

   /* mapping */
   *vecptr++ = ADMIN_REPORT_MAPPING;
   *vecptr++ = ADMIN_REPORT_MAPPING;
   *vecptr++ = ADMIN_REVISE_MAPPING;
   *vecptr++ = ADMIN_CONTROL_MAP;

   /* authorization */
   *vecptr++ = ADMIN_REPORT_AUTH_PATHS;
   *vecptr++ = ADMIN_REPORT_AUTH_PATHS;
   *vecptr++ = ADMIN_REVISE_AUTH_PATHS;
   *vecptr++ = ADMIN_CONTROL_AUTH_LOAD;

   /* authentication */
   *vecptr++ = ADMIN_REPORT_AUTH_USER;
   *vecptr++ = ADMIN_REVISE_HTA;
   *vecptr++ = ADMIN_REVISE_HTL;
   *vecptr++ = ADMIN_CONTROL_AUTH_PURGE;

   /* accept / reject */
   *vecptr++ = NetAcceptEnabled ? "\" href=" : " dbttn\" h_ref=";
   *vecptr++ = ADMIN_REPORT_ACCEPT;
   *vecptr++ = NetRejectEnabled ? "\" href=" : " dbttn\" h_ref=";
   *vecptr++ = ADMIN_REPORT_REJECT;
   *vecptr++ = NetAcceptEnabled ? "\" href=" : " dbttn\" h_ref=";
   *vecptr++ = ADMIN_REVISE_ACCEPT;
   *vecptr++ = NetRejectEnabled ? "\" href=" : " dbttn\" h_ref=";
   *vecptr++ = ADMIN_REVISE_REJECT;
   *vecptr++ = (NetAcceptEnabled || NetRejectEnabled) ?
               "\" href=" : " dbttn\" h_ref=";
   *vecptr++ = ADMIN_CONTROL_NET_ACCEPT_REJECT;
   *vecptr++ = (NetAcceptEnabled || NetRejectEnabled) ?
               "\" href=" : " dbttn\" h_ref=";
   *vecptr++ = ADMIN_CONTROL_NET_PURGE_REJECT;

   /* secure socket layer */
   if (ProtocolHttpsConfigured)
   {
      *vecptr++ = "<a class=\"abttn bttn400\" href=\"!AZ\">Service</a>";
      *vecptr++ = ADMIN_REPORT_SSL;
      if (SesolaVerifyCAConfigured)
      {
         *vecptr++ = "<a class=\"abttn bttn300\" \
href=\"!AZ?do=report&virtual=!AZ\">CA</a>";
         *vecptr++ = ADMIN_REPORT_SSL_CA;
         *vecptr++ = rqptr->ServicePtr->ServerHostPort;
         *vecptr++ = "<a class=\"abttn bttn400\" \
href=\"!AZ?do=edit\">Edit-CA</a>";
         *vecptr++ = ADMIN_REVISE_SSL_CA;
         *vecptr++ =
"<a id=\"doLoadCA\" class=\"abttn bttn400\" href=\"!AZ\">Load-CA</a>";
         *vecptr++ = ADMIN_CONTROL_SSL_CA_LOAD;
      }
      else
      {
         *vecptr++ = "<a class=\"abttn bttn300 dbttn\">CA</a>";
         *vecptr++ = "<a class=\"abttn bttn400 dbttn\">Edit-CA</a>";
         *vecptr++ = "<a class=\"abttn bttn400 dbttn\">Load-CA</a>";
      }
   }
   else
   {
      *vecptr++ = "<a class=\"abttn bttn400 dbttn\">Service</a>";
      *vecptr++ = "<a class=\"abttn bttn300 dbttn\">CA</a>";
      *vecptr++ = "<a class=\"abttn bttn400 dbttn\">Edit-CA</a>";
      *vecptr++ = "<a class=\"abttn bttn400 dbttn\">Load-CA</a>";
   }

   /* other */
#ifdef ALIGN_FAULT
   if (HttpdAlignFaultReport)
   {
      *vecptr++ = "<a class=\"abttn bttn425\" href=\"!AZ\">AlnFlt</a>";
      *vecptr++ = ADMIN_REPORT_ALIGN;
   }
   else
#endif /* ALIGN_FAULT */
      *vecptr++ = "<a class=\"abttn bttn425 dbttn\">AlnFlt</a>";

   *vecptr++ = "<a class=\"abttn bttn425\" href=\"!AZ\">Cache</a>";
   *vecptr++ = ADMIN_REPORT_CACHE;

   if (SyiClusterNodes > 1)
   {
      *vecptr++ = "<a class=\"abttn bttn425\" href=\"!AZ\">Cluster</a>";
      *vecptr++ = ADMIN_REPORT_CLUSTER;
   }
   else
      *vecptr++ = "<a class=\"abttn bttn425 dbttn\">Cluster</a>";

   *vecptr++ = "<a class=\"abttn bttn425\" href=\"!AZ\">DCL</a>";
   *vecptr++ = ADMIN_REPORT_DCL;

   *vecptr++ = "<a class=\"abttn bttn425\" href=\"!AZ\">DECnet</a>";
   *vecptr++ = ADMIN_REPORT_DECNET;

   if (TcpIpHostCachePtr)
   {
      *vecptr++ = "<a class=\"abttn bttn425\" href=\"!AZ\">Host</a>";
      *vecptr++ = ADMIN_REPORT_HOSTCACHE;
   }
   else
      *vecptr++ = "<a class=\"abttn bttn425 dbttn\">Host</a>";

   *vecptr++ = "<a class=\"abttn bttn425\" href=\"!AZ\">HTTP</a>";
   *vecptr++ = ADMIN_REPORT_HTTP;

   *vecptr++ = "<a class=\"abttn bttn425\" href=\"!AZ\">Lock</a>";
   *vecptr++ = ADMIN_REPORT_LOCK;

   *vecptr++ = "<a class=\"abttn bttn425\" href=\"!AZ\">Match</a>";
   *vecptr++ = ADMIN_REPORT_MATCH;

   *vecptr++ = "<span class=\"abttn bttn425\">\
<a style=\"text-decoration:none;color:inherit;\" href=\"!AZ\">Memory</a>\
<a style=\"text-decoration:none;color:inherit;\" \
href=\"!AZ\">&plus;</a></span>";
   *vecptr++ = ADMIN_REPORT_MEMORY;
   *vecptr++ = ADMIN_REPORT_MEMORY_PLUS;

   *vecptr++ = "<a class=\"abttn bttn425\" href=\"!AZ\">Process</a>";
   *vecptr++ = ADMIN_REPORT_PROCESS;

   if (ProxyServiceCount())
   {
      *vecptr++ = "<a class=\"abttn bttn425\" href=\"!AZ\">Proxy</a>";
      *vecptr++ = ADMIN_REPORT_PROXY;
   }
   else
      *vecptr++ = "<a class=\"abttn bttn425 dbttn\">Proxy</a>";

   *vecptr++ = "<span class=\"abttn bttn425\">\
<a style=\"text-decoration:none;color:inherit;\" href=\"!AZ\">Request</a>\
<a style=\"text-decoration:none;color:inherit;\" \
href=\"!AZ\">&plus;</a></span>";
   *vecptr++ = ADMIN_REPORT_REQUEST;
   *vecptr++ = ADMIN_REPORT_REQUEST_PLUS;

   *vecptr++ = "<span class=\"abttn bttn425\">\
<a style=\"text-decoration:none;color:inherit;\" href=\"!AZ\">System</a>\
<a style=\"text-decoration:none;color:inherit;\" \
href=\"!AZ\">&plus;</a></span>";
   *vecptr++ = ADMIN_REPORT_SYSTEM;
   *vecptr++ = ADMIN_REPORT_SYSTEM_PLUS;

   if (MappingMetaPtr->ThrottleTotal)
   {
      *vecptr++ = "<a class=\"abttn bttn425\" href=\"!AZ\">Throttle</a>";
      *vecptr++ = ADMIN_REPORT_THROTTLE;
   }
   else
      *vecptr++ = "<a class=\"abttn bttn425 dbttn\">Throttle</a>";

   if (WATCH_CAT || !Watch.Disabled)
   {
      *vecptr++ = "<a class=\"abttn bttn425\" href=\"!AZ\">WATCH</a>";
      *vecptr++ = ADMIN_REPORT_WATCH;
   }
   else
      *vecptr++ = "<a class=\"abttn bttn425 dbttn\">WATCH</a>";

   if (WebDavEnabled)
   {
      *vecptr++ = "<a class=\"abttn bttn425\" href=\"!AZ\">WebDAV</a>";
      *vecptr++ = ADMIN_REPORT_WEBDAV;
   }
   else
      *vecptr++ = "<a class=\"abttn bttn425 dbttn\">WebDAV</a>";

   *vecptr++ = "<a class=\"abttn bttn425\" href=\"!AZ\">WebSocket</a>";
   *vecptr++ = ADMIN_REPORT_WEBSOCKET;

   for (idx = 1; ActHours[idx]; idx++)
   {
      if (ActivityDeltaHours >= ActHours[idx])
      {
         *vecptr++ = "<a class=\"abttn bttn134\" href=\"!AZ?of=!UL\">!-!UL</a>";
         *vecptr++ = ADMIN_REPORT_ACTIVITY;
         *vecptr++ = ActHours[idx];
      }
      else
      if (ActivityDeltaHours > ActHours[idx-1] &&
          ActivityDeltaHours < ActHours[idx])
      {
         *vecptr++ = "<a class=\"abttn bttn134\" href=\"!AZ?of=!UL\">!-!UL</a>";
         *vecptr++ = ADMIN_REPORT_ACTIVITY;
         *vecptr++ = ActivityDeltaHours;
      }
      else
      {
         *vecptr++ = "<a class=\"abttn bttn134 dbttn\">!UL</a>";
         *vecptr++ = ActHours[idx];
      }
   }

   *vecptr++ = &rqptr->rqTime.BeginTime64;
   *vecptr++ = rqptr->rqTime.GmDateTime;
   *vecptr++ = TimeGmtString;

   /* client using the Server Admin */
   if (MATCH8 (&rqptr->ClientPtr->IpAddressString,
               rqptr->ClientPtr->Lookup.HostName))
   {
      /* client IP address DNS lookup is disabled */
      cptr = AdminLookYouUp (&rqptr->ClientPtr->IpAddress);
      if (cptr && *cptr)
      {
         *vecptr++ = "!AZ (!AZ) !&@";
         *vecptr++ = cptr;
      }
      else
         *vecptr++ = "!AZ !&@";
   }
   else
   {
      *vecptr++ = "!AZ (!AZ) !&@";
      *vecptr++ = rqptr->ClientPtr->Lookup.HostName;
   }
   *vecptr++ = &rqptr->ClientPtr->IpAddressString;
   if (HTTP2_REQUEST(rqptr))
      *vecptr++ = "HTTP/2<span id=\"pingRTT\"></span>";
   else
   if (rqptr->rqResponse.HttpVersion == HTTP_VERSION_1_1)
      *vecptr++ = "HTTP/1.1<span id=\"pingRTT\"></span>";
   else
      *vecptr++ = "HTTP/1.0<span id=\"pingRTT\"></span>";

   /* informational accounting */
   *vecptr++ = SysInfo.NodeName;
   *vecptr++ = &SystemUpTime64;
   if (NodeInstanceCount > 1)
      *vecptr++ = HttpdProcess.PrcNam;
   else
      *vecptr++ = "Process";
   *vecptr++ = &ProcessUpTime64;
   *vecptr++ = &ImageUpTime64;
   *vecptr++ = JpiCpuTime / 8640000;             /* CPU day */
   *vecptr++ = (JpiCpuTime % 8640000) / 360000;  /* CPU hour */
   *vecptr++ = (JpiCpuTime % 360000) / 6000;     /* CPU minute */
   *vecptr++ = (JpiCpuTime % 6000 ) / 100;       /* CPU second */
   *vecptr++ = JpiCpuTime % 100;                 /* CPU 10 milliseconds */

   *vecptr++ = acptr->CurrentConnected[HTTP12];
   *vecptr++ = acptr->ConnectPeak[HTTP12];
   *vecptr++ = acptr->ConnectCount[HTTP12];

   *vecptr++ = acptr->CurrentProcessing[HTTP12];
   *vecptr++ = acptr->ProcessingPeak[HTTP12];
   *vecptr++ = acptr->ProcessingTotalCount[HTTP12];
   BytesTotal64 = acptr->BytesRawRx64[HTTP12] +
                  acptr->BytesRawTx64[HTTP12];
   *vecptr++ = &BytesTotal64;

   if (HttpdGblSecPtr->AuthSkelKeyHttpdTickSecond)
   {
      *vecptr++ = "<tr><th class=\"talft\">SKELKEY</th>"
                  "<td colspan=\"2\">!AZ!&@ (!AZ)</td></tr>\n";
      *vecptr++ = HttpdGblSecPtr->AuthSkelKeyUserName;
      if (HttpdGblSecPtr->AuthSkelKeyVmsName[0])
      {
         *vecptr++ = "=!AZ";
         *vecptr++ = HttpdGblSecPtr->AuthSkelKeyVmsName;
      }
      else
         *vecptr++ = "";
      *vecptr++ = WatchToGo (HttpdGblSecPtr->AuthSkelKeyHttpdTickSecond - HttpdTickSecond);
   }
   else
      *vecptr++ = "";

   if (Watch.SecondsToGo)
   {
      *vecptr++ = "<tr><th class=\"talft\">WATCH</th>"
                  "<td colspan=\"2\">!AZ (!UL bytes)</td></tr>\n";
      *vecptr++ = WatchToGo(Watch.SecondsToGo);
      *vecptr++ = Watch.ByteCount;
   }
   else
      *vecptr++ = "";

   if (WatchQuotasAlert[0] || LoggingFileError)
      *vecptr++ = "<tr><td colspan=\"3\" style=\"text-align:center;\">";
   else
      *vecptr++ = "";

   if (WatchQuotasAlert[0])
   {
      *vecptr++ = "<div class=\"alert\">!AZ</div>";
      *vecptr++ = WatchQuotasAlert;
   }
   else
      *vecptr++ = "";

   if (LoggingFileError)
      *vecptr++ = "<div class=\"alert\">Problem writing access logs!</div>";
   else
      *vecptr++ = "";

   if (WatchQuotasAlert[0] || LoggingFileError)
      *vecptr++ = "</td></tr>\n";
   else
      *vecptr++ = "";

   /* server control menu items */
   *vecptr++ = ADMIN_CONTROL_RESTART;
   *vecptr++ = ADMIN_CONTROL_RESTART_NOW;
   *vecptr++ = ADMIN_CONTROL_RESTART_QUIET;
   *vecptr++ = ADMIN_CONTROL_EXIT;
   *vecptr++ = ADMIN_CONTROL_EXIT_NOW;

   /* logging control menu items */
   if (LoggingEnabled)
   {
      *vecptr++ =
"<a class=\"abttn bttn200 dbttn\">On</a>\
<a id=\"doLogOff\" class=\"abttn bttn200\" href=\"!AZ\">Off</a>\
<a id=\"doLogFlush\" class=\"abttn bttn300\" href=\"!AZ\">Flush</a>";
      *vecptr++ = ADMIN_CONTROL_LOG_CLOSE;
      *vecptr++ = ADMIN_CONTROL_LOG_FLUSH;
   }
   else
   {
      *vecptr++ =
"<a id=\"doLogOn\" class=\"abttn bttn200\" href=\"!AZ\">On</a>\
<a class=\"abttn bttn200 dbttn\">Off</a>\
<a class=\"abttn bttn300 dbttn\">Flush</a>";
      *vecptr++ = ADMIN_CONTROL_LOG_OPEN;
   }

   /* cache control menu items */
   if (CacheEnabled)
   {
      *vecptr++ =
"<a class=\"abttn bttn200 dbttn\">On</a>\
<a id=\"doCacheOff\" class=\"abttn bttn200\" href=\"!AZ\">Off</a>\
<a id=\"doCachePurge\" class=\"abttn bttn300\" href=\"!AZ\">Purge</a>";
      *vecptr++ = ADMIN_CONTROL_CACHE_OFF;
      *vecptr++ = ADMIN_CONTROL_CACHE_PURGE;
   }
   else
   {
      *vecptr++ =
"<a id=\"doCacheOn\" class=\"abttn bttn200\" href=\"!AZ\">On</a>\
<a class=\"abttn bttn200 dbttn\">Off</a>\
<a class=\"abttn bttn300 dbttn\">Flush</a>";
      *vecptr++ = ADMIN_CONTROL_CACHE_ON;
   }

   InstanceMutexLock (INSTANCE_MUTEX_HTTPD);
   InstanceStartupMax = HttpdGblSecPtr->InstanceStartupMax;
   InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);

   if (!InstanceStartupMax)
   {
      *vecptr++ =
"<a id=\"doInstMax\" class=\"abttn bttn290\" href=\"!AZ/max\">Max</a>";
      *vecptr++ = ADMIN_CONTROL_INSTANCE;
   }
   else
      *vecptr++ = "<a class=\"abttn bttn290 dbttn\">Max</a>";

   if (InstanceStartupMax != INSTANCE_PER_CPU)
   {
      *vecptr++ =
"<a id=\"doInstCPU\" class=\"abttn bttn290\" href=\"!AZ/cpu\">CPU</a>";
      *vecptr++ = ADMIN_CONTROL_INSTANCE;
   }
   else
      *vecptr++ = "<a class=\"abttn bttn290 dbttn\">CPU</a>";

   FaoCheck (sizeof(FaoVector), &FaoVector, vecptr, FI_LI);
   status = FaolToNet (rqptr, BeginPageFao, &FaoVector);
   if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);

   for (cnt = 1; cnt <= INSTANCE_MAX; cnt++)
   {
      vecptr = FaoVector;
      if (cnt == InstanceStartupMax)
      {
         *vecptr++ = cnt;
         status = FaolToNet (rqptr,
"<a class=\"abttn bttn090 dbttn\">!UL</a>",
                             &FaoVector);
      }
      else
      {
         *vecptr++ = cnt;
         *vecptr++ = ADMIN_CONTROL_INSTANCE;
         *vecptr++ = cnt;
         status = FaolToNet (rqptr,
"<a id=\"doInst!UL\" class=\"abttn bttn090\" href=\"!AZ/!UL\">!-!UL</a>",
                             &FaoVector);
      }
      if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);
      if (cnt == 2) FaolToNet (rqptr, "<br>\n", NULL);
   }

   vecptr = FaoVector;

   /* instance active/pass control menu items */
   if (InstanceNodeCurrent == 1)
   {
      *vecptr++ =
"<a class=\"abttn bttn400 dbttn\">Active</a>\
<a class=\"abttn bttn400 dbttn\">Passive</a>";
   }
   else
   if (NetInstancePassive)
   {
      *vecptr++ =
"<a id=\"doActive\" class=\"abttn bttn400\" href=\"!AZ\">Active</a>\
<a class=\"abttn bttn400 dbttn\">Passive</a>";
      *vecptr++ = ADMIN_CONTROL_INSTANCE_ACTIVE;
   }
   else
   {
      *vecptr++ =
"<a class=\"abttn bttn400 dbttn\">Active</a>\
<a id=\"doPassive\" class=\"abttn bttn400\" href=\"!AZ\">Passive</a>";
      *vecptr++ = ADMIN_CONTROL_INSTANCE_PASSIVE;
   }

   status = FaolToNet (rqptr, EndInstanceFao, &FaoVector);
   if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);

   InstanceStatusAdminReport (rqptr);

   vecptr = FaoVector;
   *vecptr++ = GenerateCspNonce (rqptr);
   *vecptr++ = AdminRefresh (rqptr);

   status = FaolToNet (rqptr, EndPageFao, &FaoVector);
   if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);

   if (NodeInstancePtr) VmFree (NodeInstancePtr, FI_LI);
   if (ClusterInstancePtr) VmFree (ClusterInstancePtr, FI_LI);

   rqptr->rqResponse.PreExpired = PRE_EXPIRE_ADMIN;
   ResponseHeader200 (rqptr, "text/html", &rqptr->NetWriteBufferDsc);

   AdminEnd (rqptr);
}

/*****************************************************************************/
/*
Provide a menu of HTTPd per-service administration functions.
*/ 

void AdminVirtualServerMenu (REQUEST_STRUCT *rqptr)

{
   char  BeginPageFao [] =
"!AZ\
<html>\n\
<head>\n\
!AZ\
<title>WASD !AZ ... Virtual Server Administration</title>\n\
</head>\n\
!AZ\n\
<h2><nobr>WASD !AZ</nobr></h2>\n\
<h3>Virtual Server Administration</h3>\n\
<p>\n\
\
<table class=\"rghtlft\">\n\
<tr><td>\n\
\
<table class=\"rghtlft\">\n\
\
<tr><th class=\"sbttl talft\">Time</th>\
<th>Local:&nbsp;</th><td>!20&W</td></tr>\n\
<tr><th colspan=\"2\">Universal:</th><td>!AZ (!AZ)</td></tr>\n\
<tr><th colspan=\"2\">System-Up:</th><td>!%D</td></tr>\n\
<tr><th></th></tr>\n\
\
<tr>\n\
<th class=\"sbttl\" colspan=\"2\">Authentication&nbsp;&nbsp;HTA:</th>\n\
<td>[<a href=\"!AZ\">Access</a>]</td>\n\
</tr>\n\
<tr>\n\
<th colspan=\"2\">HTL:</th>\n\
<td>[<a href=\"!AZ\">Access</a>]</td>\n\
</tr>\n\
<tr>\n\
<th colspan=\"2\">Cache:</th>\n\
<td>[<a href=\"!AZ\">Purge</a>]</td>\n\
</tr>\n\
\
</table>\n\
\
</td></tr>\n\
</table>\n\
\
</form>\n\
\
</div>\n\
</body>\n\
</html>\n";

   static unsigned long  LibDeltaHours = LIB$K_DELTA_HOURS;

   static unsigned long  Pid = 0;
   static int64  Time64,
                 JpiLoginTime64,
                 SystemUpTime64;
   static VMS_ITEM_LIST3  JpiItems [] =
   {
      { sizeof(JpiLoginTime64), JPI$_LOGINTIM, &JpiLoginTime64, 0 },
      {0,0,0,0}
   };

   int  status;
   unsigned short  Length;
   unsigned long  *vecptr;
   unsigned long  FaoVector [32];
   IO_SB  IOsb;

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD__OTHER))
      WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER,
                 "AdminVirtualServerMenu()");

   status = sys$getjpiw (EfnWait, &Pid, 0, &JpiItems, &IOsb, 0, 0);
   if (VMSok (status)) status = IOsb.Status;
   if (VMSnok (status))
   {
      rqptr->rqResponse.ErrorTextPtr = "sys$getjpiw()";
      ErrorVmsStatus (rqptr, status, FI_LI);
      AdminEnd (rqptr);
      return;
   }

   sys$gettim (&Time64);
   SystemUpTime64 = SysInfo.BootTime64 - Time64;

   vecptr = FaoVector;

   *vecptr++ = WASD_DOCTYPE;
   *vecptr++ = HtmlMetaInfo (rqptr, NULL);
   *vecptr++ = ServerHostPort;

   *vecptr++ = ADMIN_BODY_TAG;
   *vecptr++ = ServerHostPort;

   *vecptr++ = &rqptr->rqTime.BeginTime64;
   *vecptr++ = rqptr->rqTime.GmDateTime;
   *vecptr++ = TimeGmtString;
   *vecptr++ = &SystemUpTime64;

   *vecptr++ = ADMIN_VS_REVISE_HTA;
   *vecptr++ = ADMIN_VS_REVISE_HTL;
   *vecptr++ = ADMIN_VS_CONTROL_AUTH_PURGE;

   FaoCheck (sizeof(FaoVector), &FaoVector, vecptr, FI_LI);
   status = FaolToNet (rqptr, BeginPageFao, &FaoVector);
   if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);

   rqptr->rqResponse.PreExpired = PRE_EXPIRE_ADMIN;
   ResponseHeader200 (rqptr, "text/html", &rqptr->NetWriteBufferDsc);

   AdminEnd (rqptr);
}

/*****************************************************************************/
/*
The 'InstanceList' comprises a string containing instance process names in the
format 'node::WASD:port' (e.g. DELTA::WASD:80") separated by newlines (the
string is generated by InstanceLockList()).  Parse this list using the process
names to interogate locks taken out by each process to contain their
per-instance administration IP address and port.  Use this information to
return a count of how many such ports are available.
*/ 

int AdminMenuInstanceCount
(
REQUEST_STRUCT *rqptr,
char *InstanceList
)
{
   int  status,
        AdminPortCount;
   char  ch;
   char  *cptr, *sptr;

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD__OTHER))
      WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER,
                 "AdminMenuInstanceCount() !&Z", InstanceList);

   AdminPortCount = 0;
   cptr = InstanceList;
   while (*cptr)
   {
      sptr = cptr;
      while (*cptr && *cptr != '\n') cptr++;
      ch = *cptr;
      if (*cptr) *cptr = '\0';
      status = InstanceSocketForAdmin (sptr, NULL);
      if (VMSok (status)) AdminPortCount++;
      if (*cptr = ch) cptr++;
   }
   return (AdminPortCount);
}

/*****************************************************************************/
/*
The 'InstanceList' comprises a string containing instance process names in the
format 'node::WASD:port' (e.g. DELTA::WASD:80") separated by newlines (the
string is generated by InstanceLockList()).  Parse this list using the process
names to interogate locks taken out by each process to contain their
per-instance administration IP address and port.  Use this information to
generate links to each of the instances.
*/ 

void AdminMenuInstanceList
(
REQUEST_STRUCT *rqptr,
char *InstanceList
)
{
   BOOL  InsrancePassive;
   int  status;
   unsigned short  IpPort;
   unsigned long  *vecptr;
   unsigned long  FaoVector [16];
   char  ch;
   char  *aptr, *cptr, *sptr, *zptr;

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD__OTHER))
      WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER,
                 "AdminMenuInstanceList() !&Z", InstanceList);

   cptr = InstanceList;
   while (*cptr)
   {
      sptr = cptr;
      while (*cptr && *cptr != '\n') cptr++;
      ch = *cptr;
      if (*cptr) *cptr = '\0';
      vecptr = FaoVector;
      status = InstanceSocketForAdmin (sptr, &IpPort);
      if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);

      aptr = "";
      if (NetInstancePassive)
      {
         /* node::prcnam */
         for (zptr = sptr; *zptr != ':'; zptr++);
         if (*zptr)
         {
            *zptr = '\0';
            if (!strcmp (sptr, SysInfo.NodeName))
            {
               if (InstanceNodeSupervisor && 
                   !strcmp (zptr+2, HttpdProcess.PrcNam))
                  aptr = "&nbsp;&nbsp;<i>active</i>";
               else
                  aptr = "&nbsp;&nbsp;<i>passive</i>";
            }
            *zptr = ':';
         }
      }

      if (IpPort)
      {
         *vecptr++ = "<a href=\"!AZ//!#AZ:!UL!AZ\">!AZ</a>!AZ<br>\n";
         *vecptr++ = rqptr->ServicePtr->RequestSchemeNamePtr;
         *vecptr++ = strchr(sptr,':') - sptr;
         *vecptr++ = sptr;
         *vecptr++ = IpPort;
         *vecptr++ = rqptr->rqHeader.PathInfoPtr;
      }
      else
         *vecptr++ = "!AZ!AZ<br>\n";
      *vecptr++ = sptr;
      *vecptr++ = aptr;

      FaoCheck (sizeof(FaoVector), &FaoVector, vecptr, FI_LI);
      status = FaolToNet (rqptr, "!&@", &FaoVector);
      if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);

      if (*cptr = ch) cptr++;
   }
}

/*****************************************************************************/
/*
Return report on the HTTPd server's activities.
*/ 

void AdminReportServerStats (REQUEST_STRUCT *rqptr)

{
/* ALTPRI and SETPRI synonyms? */
#define IMAG_PRIV_0_ALLOWED \
    (~(PRV$M_ALTPRI | PRV$M_CMKRNL | PRV$M_DETACH | PRV$M_OPER |   \
       PRV$M_PRMGBL | PRV$M_PRMMBX | PRV$M_SHMEM  | PRV$M_SYSGBL | \
       PRV$M_SYSNAM | PRV$M_SYSLCK | PRV$M_SYSPRV | PRV$M_PSWAPM | \
       PRV$M_WORLD))

#define IMAG_PRIV_1_ALLOWED (~(0x40 /* PRV$M_SECURITY */))


   static char BeginPageFao [] =
"<p><table class=\"ctgry\">\n\
<tr><th class=\"ctttl\">Environment!AZ</th></tr>\n\
<tr><td>\n\
<table class=\"rghtlft\">\n\
<tr><th>Version:</th><td>!AZ</td></tr>\n\
<tr><th>Build:</th><td>!AZ</td></tr>\n\
<tr><th>SSL:</th><td>!AZ</td></tr>\n\
!&@\
<tr><th>TCP/IP:</th><td>!AZ</td></tr>\n\
<tr><th>System:</th>\
<td>!AZ with !UL CPU!%s and !AZ running VMS !AZ</td></tr>\n\
<tr><th>Startup:</th><td>!&;AZ</td></tr>\n\
<tr><th style=\"font-size:85%;\">!#AZ:</th><td>!&;AZ</td></tr>\n\
</table>\n\
</td></tr>\n\
</table>\n\
\
<p><table class=\"ctgry\">\n\
<tr><th class=\"ctttl\">Instances</th></tr>\n\
<tr><td>\n\
<table class=\"lftlft\">\n\
<tr><th><u>Node</u></th><th><u>Cluster</u></th></tr>\n\
<tr><td>!AZ</td><td>!AZ</td></tr>\n\
<tr><td colspan=\"2\">!AZ</td></tr>\n\
</table>\n\
</td></tr>\n\
</table>\n\
\
<p><table class=\"ctgry\">\n\
<tr><th class=\"ctttl\">!&?Server\rInstance\r Process</th></tr>\n\
<tr><td>\n\
<table class=\"rghtlft\">\n\
<tr>\
<th>Name:</th><td>!AZ</td>\
<th>PID:</th><td><a href=\"!AZ?pid=!8XL!&@\">!8XL</a></td>\
<th>User:</th><td>!AZ</td>\
</tr>\n\
<tr>\
<th>AuthPriv:</th><td>!AZ</td>\
<th>ImagPriv:</th><td>!AZ</td>\
<th>CurPriv:</th><td>!AZ</td>\
</tr>\n\
<tr>\
<th><nobr>Last Exit:</nobr></th><td>!AZ</td>\
<th><nobr>Exit Time:</nobr></th><td><nobr>!AZ</nobr></td>\
<th><nobr>Exit PID:</nobr></th><td>!AZ</td>\
</tr>\n\
<tr>\
<th>Image:</th><td>!%D</td>\
<th>Process:</th><td>!%D</td>\
<th>CPU:</th><td>!UL !2ZL:!2ZL:!2ZL.!2ZL</td>\n\
</tr>\n\
<tr>\
<th>Pg.Faults:</th><td>!UL</td>\n\
<th>Startup:</th><td>!UL</td>\
<th>Mode:</th><td>!AZ</td>\
</tr>\n\
<tr>\
<th>WsSize:</th><td>!UL (!ULMB)</td>\
<th>WsPeak:</th><td>!UL (!ULMB)</td>\n\
<th>VirtPeak:</th><td>!UL (!ULMB)</td>\
</tr>\n\
<tr>\
<th>AST:</th><td>!UL&nbsp;/&nbsp;!UL&nbsp;(!UL%)</td>\
<th>BIO:</th><td>!UL&nbsp;/&nbsp;!UL&nbsp;(!UL%)</td>\
<th>BYT:</th><td>!UL&nbsp;/&nbsp;!UL&nbsp;(!UL%)</td>\
</tr>\n\
<tr>\
<th>DIO:</th><td>!UL&nbsp;/&nbsp;!UL&nbsp;(!UL%)</td>\
<th>ENQ:</th><td>!UL&nbsp;/&nbsp;!UL&nbsp;(!UL%)</td>\
<th>FIL:</th><td>!UL&nbsp;/&nbsp;!UL&nbsp;(!UL%)</td>\
</tr>\n\
<tr>\
<th>PGFL:</th><td>!UL&nbsp;/&nbsp;!UL<br>(!UL/!ULMB !UL%)</td>\
<th>PRC:</th><td>!&@</td>\
<th>TQ:</th><td>!UL&nbsp;/&nbsp;!UL&nbsp;(!UL%)</td>\n\
</tr>\n\
<tr><th>Input:</th>\
<td colspan=\"3\"><font size=\"-1\">!&@</font></td></tr>\n\
<tr><th>Output:</th>\
<td colspan=\"3\"><font size=\"-1\">!&@</font></td></tr>\n\
!&@\
</table>\n\
</td></tr>\n\
</table>\n\
\
<p><table class=\"ctgry\">\n\
<tr><th class=\"ctttl\">System Resources</th></tr>\n\
<tr><td>\n\
<table class=\"rghtlft\">\n\
<tr><td></td><td style=\"font-size:90%;\"><b>GBLPAGES</b></td>\
<td style=\"font-size:90%;\"><b>GBLSECTIONS</b></td></tr>\n\
<tr><th>Temporary:</th><td>!UL</td><td>!UL</td></tr>\n\
<tr><th>Permanent:</th><td>!UL</td><td>!UL</td></tr>\n\
<tr><th>Total:</th><td>!UL</td><td>!UL</td></tr>\n\
</table>\n\
</td></tr>\n\
</table>\n";

   static char  StatisticsFao [] =
"<style type=\"text/css\">\n\
table.request th { padding-top:0; padding-bottom:0; }\n\
table.request td { padding-top:0; padding-bottom:0; }\n\
</style>\n\
<p><table class=\"ctgry request\">\n\
<tr><th class=\"ctttl\">Request Processing</th></tr>\n\
<tr><td>\n\
<table class=\"rghtlft\">\n\
<tr><td>\n\
\
<table>\n\
<tr><th class=\"sbttl\">Connection</th></tr>\n\
<tr><th>Total:</th><td>!&L</td></tr>\n\
<tr><th>Current:</th><td>!&L</td></tr>\n\
<tr><th>Peak:</th><td>!&L</td></tr>\n\
<tr><th>Busy:</th><td>!&L</td></tr>\n\
<tr><th>HTTP2&nbsp;&nbsp;/Total:</th>\
<td>!&L</td><td>(!UL%)</td></tr>\n\
<tr><th>/Current:</th>\
<td>!&L</td><td>(!UL%)</td></tr>\n\
<tr><th>/Peak:</th><td>!&L</td></tr>\n\
<tr><th>Persistent&nbsp;&nbsp;/Current:</th><td>!&L</td></tr>\n\
<tr><th>/Peak:</th><td>!&L</td></tr>\n\
<tr><th>IPv4:</th><td>!&L</td><td>(!UL%)</td></tr>\n\
<tr><th>IPv6:</th><td>!&L</td><td>(!UL%)</td></tr>\n\
<tr><th>SSL:</th><td>!&L</td><td>(!UL%)</td></tr>\n\
<tr><th>Total&nbsp;&nbsp;/Rx:</th><td colspan=\"2\">!&,@UQ</td></tr>\n\
<tr><th>/Tx:</th><td colspan=\"2\">!&,@UQ</td></tr>\n\
<tr><th>Error&nbsp;&nbsp;/Rx:</th><td>!&L</td></tr>\n\
<tr><th>/Tx:</th><td>!&L</td></tr>\n\
<tr><th>Accept:</th><td>!&L</td></tr>\n\
<tr><th>Reject:</th><td>!&L</td></tr>\n\
\
</table>\n\
\
</td><td>\n\
\
<table>\n\
<tr><th class=\"sbttl\">Request</th></tr>\n\
<tr><th>Total:</th><td>!&L</td></tr>\n\
<tr><th>Current:</th><td>!&L</td></tr>\n\
<tr><th>Peak:</th><td>!&L</td></tr>\n\
<tr><th>Busy:</th><td>!&L</td></tr>\n\
<tr><th>HTTP2&nbsp;&nbsp;/Total:</th>\
<td>!&L</td><td>(!UL%)</td></tr>\n\
<tr><th>/Current:</th>\
<td>!&L</td><td>(!UL%)</td></tr>\n\
<tr><th>/Peak:</th><td>!&L</td></tr>\n\
<tr><th>WebSocket&nbsp;&nbsp;/Current:</th><td>!&L</td></tr>\n\
<tr><th>/Total:</th><td>!&L</td></tr>\n\
<tr><th>Raw&nbsp;&nbsp;/Current:</th><td>!&L</td>\
<td>(!UL%)</td></tr>\n\
<tr><th>/Total:</th><td>!&L</td><td>(!UL%)</td></tr>\n\
<tr><th>SSL:</th>\
<td>!&L</td><td>(!UL%)</td></tr>\n\
<tr><th>Throttle&nbsp;&nbsp;/Queued:</th><td>!&L</td></tr>\n\
<tr><th>/Processing:</th><td>!&L</td></tr>\n\
<tr><th>/Busy:</th><td>!UL%</td></tr>\n\
<tr><th>Persistent&nbsp;&nbsp;/Total:</th><td>!&L</td></tr>\n\
<tr><th>/Max:</th><td>!&L</td></tr>\n\
<tr><th>Pipeline&nbsp;&nbsp;/Total:</th><td>!&L</td></tr>\n\
<tr><th>/Max:</th><td>!&L</td>\
<td>!#AZ</td></tr>\n\
\
</table>\n\
\
</td><td rowspan=\"3\">\n\
<table>\n\
<tr><th class=\"sbttl\">HTTP</th></tr>\n\
<tr><th>2:</th><td>!&L</td><td>(!UL%)</td></tr>\n\
<tr><th>1.1:</th><td>!&L</td><td>(!UL%)</td></tr>\n\
<tr><th>1.0:</th><td>!&L</td><td>(!UL%)</td></tr>\n\
<tr><th>0.9:</th><td>!&L</td><td>(!UL%)</td></tr>\n\
<tr><td>&nbsp;</td></tr>\n\
\
<tr><th class=\"sbttl\">Method</th></tr>\n\
<tr><th>CONNECT:</th><td>!&L</td></tr>\n\
<tr><th>COPY:</th><td>!&L</td></tr>\n\
<tr><th>DELETE:</th><td>!&L</td></tr>\n\
<tr><th>GET:</th><td>!&L</td></tr>\n\
<tr><th>HEAD:</th><td>!&L</td></tr>\n\
<tr><th>LOCK:</th><td>!&L</td></tr>\n\
<tr><th>MKCOL:</th><td>!&L</td></tr>\n\
<tr><th>MOVE:</th><td>!&L</td></tr>\n\
<tr><th>OPTIONS:</th><td>!&L</td></tr>\n\
<tr><th>POST:</th><td>!&L</td></tr>\n\
<tr><th>PROPFIND:</th><td>!&L</td></tr>\n\
<tr><th>PROPPATCH:</th>\
<td>!&L</td></tr>\n\
<tr><th>PUT:</th><td>!&L</td></tr>\n\
<tr><th>TRACE:</th><td>!&L</td></tr>\n\
<tr><th>UNLOCK:</th><td>!&L</td></tr>\n\
<tr><th>Extension:</th><td>!&L</td></tr>\n\
<tr><th>(SSH):</th><td>!&L</td></tr>\n\
</table>\n\
</td></tr>\n\
\
<tr><td style=\"margin-top:0.5em;\"></td></tr>\n\
<tr><td colspan=\"2\">\n\
<table cellpadding=\"0\" cellspacing=\"5\" border=\"0\">\n\
\
<tr><td>\n\
\
<table>\n\
<tr><th colspan=\"10\" class=\"sbttl\" style=\"text-align:center;\">\
Response Status</th></tr>\n\
<tr>\
<th>1nn:</th><td>!&L</td>\
<th>&nbsp;&nbsp;2nn:</th><td>!&L</td>\
<th>&nbsp;&nbsp;3nn:</th><td>!&L</td>\
<th>&nbsp;&nbsp;4nn:</th><td>!&L</td>\
<th>&nbsp;&nbsp;5nn:</th><td>!&L</td>\
</tr>\n\
<tr></tr>\n\
<tr>\
<th>100:</th><td>!&L</td>\
<th>200:</th><td>!&L</td>\
<th>300:</th><td>!&L</td>\
<th>400:</th><td>!&L</td>\
<th>500:</th><td>!&L</td>\
</tr>\n\
<tr>\
<th>101:</th><td>!&L</td>\
<th>201:</th><td>!&L</td>\
<th>301:</th><td>!&L</td>\
<th>401:</th><td>!&L</td>\
<th>501:</th><td>!&L</td>\
</tr>\n\
<tr>\
<th>102:</th><td>!&L</td>\
<th>202:</th><td>!&L</td>\
<th>302:</th><td>!&L</td>\
<th>402:</th><td>!&L</td>\
<th>502:</th><td>!&L</td>\
</tr>\n\
<tr>\
<td colspan=\"2\"></td>\
<th>203:</th><td>!&L</td>\
<th>303:</th><td>!&L</td>\
<th>403:</th><td>!&L</td>\
<th>503:</th><td>!&L</td>\
</tr>\n\
<tr>\
<td colspan=\"2\"></td>\
<th>204:</th><td>!&L</td>\
<th>304:</th><td>!&L</td>\
<th>404:</th><td>!&L</td>\
<th>504:</th><td>!&L</td>\
</tr>\n\
<tr>\
<td colspan=\"2\"></td>\
<th>205:</th><td>!&L</td>\
<th>305:</th><td>!&L</td>\
<th>405:</th><td>!&L</td>\
<th>505:</th><td>!&L</td>\
</tr>\n\
<tr>\
<td colspan=\"2\"></td>\
<th>206:</th><td>!&L</td>\
<th>306:</th><td>!&L</td>\
<th>406:</th><td>!&L</td>\
</tr>\n\
<tr>\
<td colspan=\"2\"></td>\
<th>207:</th><td>!&L</td>\
<th>307:</th><td>!&L</td>\
<th>407:</th><td>!&L</td>\
<th>507:</th><td>!&L</td>\
</tr>\n\
<tr>\
<td colspan=\"6\"></td>\
<th>408:</th><td>!&L</td>\
</tr>\n<tr>\
<td colspan=\"4\"></td>\
<th>409:</th><td>!&L</td>\
<th>410:</th><td>!&L</td>\
<th>411:</th><td>!&L</td>\
</tr>\n<tr>\
<td colspan=\"4\"></td>\
<th>412:</th><td>!&L</td>\
<th>413:</th><td>!&L</td>\
<th>414:</th><td>!&L</td>\
</tr>\n<tr>\
<td colspan=\"4\"></td>\
<th>415:</th><td>!&L</td>\
<th>416:</th><td>!&L</td>\
<th>417:</th><td>!&L</td>\
</tr>\n<tr>\
<td colspan=\"4\"></td>\
<th>418:</th><td>!&L</td>\
<th>421:</th><td>!&L</td>\
<th>422:</th><td>!&L</td>\
</tr>\n<tr>\
<td colspan=\"4\"></td>\
<th>423:</th><td>!&L</td>\
<th>424:</th><td>!&L</td>\
</tr>\n<tr>\
<th>000:</th><td>!&L</td>\
<th>???:</th><td>!&L</td>\
</tr>\n\
</table>\n\
\
</td><td>&nbsp;</td><td>\n\
\
<table>\n\
<tr><th class=\"sbttl\" style=\"text-decoration:none;\">\
<sup>*</sup><u>Duration</u></th><td></td></tr>\n\
<tr><th>Min:</th><td colspan=\"2\">!AZ</td></tr>\n\
<tr><th>Max:</th><td colspan=\"2\">!AZ</td></tr>\n\
<tr><th>Ave:</th><td colspan=\"2\">!AZ</td></tr>\n\
<tr><th>Total:</th><td colspan=\"2\">!AZ</td></tr>\n\
\
<tr><td>&nbsp;</td></tr>\n\
\
<tr><th class=\"sbttl\" style=\"text-decoration:none;\">\
<sup>*</sup><u>Bytes/Second</u></th><td></td></tr>\n\
<tr><th>Min&nbsp;&nbsp;/Bps:</th><td>!&L</td></tr>\n\
<tr><th>/Bytes:</th><td>!&,@UQ</td></tr>\n\
<tr><th>/Duration:</th><td>!AZ</td></tr>\n\
<tr><th>Max&nbsp;&nbsp;/Bps:</th><td>!&L</td></tr>\n\
<tr><th>/Bytes:</th><td>!&,@UQ</td></tr>\n\
<tr><th>/Duration:</th><td>!AZ</td></tr>\n\
<tr><th>Ave&nbsp;&nbsp;/Bps:</th><td>!&L</td></tr>\n\
<tr><th>/Bytes:</th><td>!&L</td></tr>\n\
<tr><th>/Duration:</th><td>!AZ</td></tr>\n\
<tr><td colspan=\"2\" style=\"font-size:90%;\">\
<sup>*</sup><i>excludes WebSocket and<br>&nbsp;\
Proxy-Tunnel requests</i></td></tr>\n\
</table>\n\
\
</td></tr>\n\
\
</table>\n\
</td></tr>\n\
</table>\n\
</td></tr>\n\
</table>\n\
\
<p><table class=\"ctgry\">\n\
<tr><th class=\"ctttl\">Processing Modules</th></tr>\n\
<tr><td>\n\
<table class=\"rghtlft\">\n\
<tr><td>\n\
<table class=\"rghtlft\">\n\
<tr><th>Admin:</th><td>!&L</td></tr>\n\
<tr><th>Cache&nbsp;&nbsp;/Load:</th><td>!&L</td></tr>\n\
<tr><th>/Hit:</th><td>!&L</td></tr>\n\
<tr><th>/304:</th><td>!&L</td></tr>\n\
<tr><th>Directory:</th><td>!&L</td></tr>\n\
<tr><th>File&nbsp;&nbsp;/All:</th><td>!&L</td></tr>\n\
<tr><th>/304:</th><td>!&L</td></tr>\n\
<tr><th>Put:</th><td>!&L</td></tr>\n\
<tr><th>Proxy:</th><td>!&L</td></tr>\n\
<tr><th>SSI:</th><td>!&L</td></tr>\n\
<tr><th>Update:</th><td>!&L</td></tr>\n\
<tr><th>WebDAV:</th><td>!&L</td></tr>\n\
<tr><th>WebSocket:</th><td>!&L</td></tr>\n\
<tr><th></th></tr>\n\
<tr><th><i>none</i>:</th><td>!&L</td></tr>\n\
</table>\n\
</td><td>\n\
<table class=\"rghtlft\">\n\
<tr><th class=\"sbttl\">DCL</th></tr>\n\
<tr><td colspan=\"2\">&nbsp;<b>As:</b>&nbsp;!AZ</td></tr>\n\
<tr><th>CGI:</th><td>!&L</td></tr>\n\
<tr><th>CGIplus&nbsp;&nbsp;/All:</th><td>!&L</td></tr>\n\
<tr><th>/Reused:</th><td>!&L</td></tr>\n\
<tr><th>RTE&nbsp;&nbsp;/All:</th><td>!&L</td></tr>\n\
<tr><th>/Reused:</th><td>!&L</td></tr>\n\
<tr><th>Autoscript:</th><td>!&L</td></tr>\n\
<tr><th>CLI:</th><td>!&L</td></tr>\n\
<tr><th>Proctor:</th><td>!&L</td></tr>\n\
<tr><th>WebSocket:</th><td>!&L</td></tr>\n\
<tr><th>$CREPRC:</th><td>!&L</td></tr>\n\
<tr><th>&nbsp;&nbsp;$PERSONA!&?_MACRO\r\r&nbsp;&nbsp;/All:</th><td>!&L</td></tr>\n\
<tr><th>/Default:</th><td>!&L</td></tr>\n\
<tr><th>/Invalid:</th><td>!&L</td><td></td></tr>\n\
<tr><th>/Privileged:</th><td>!&L</td></tr>\n\
<tr><th>$FORCEX:</th><td>!&L</td></tr>\n\
<tr><th>$DELPRC:</th><td>!&L</td></tr>\n\
</table>\n\
\
</td><td>\n\
<table class=\"rghtlft\">\n\
<tr><th class=\"sbttl\">DECnet</th></tr>\n\
<tr><td colspan=\"2\">&nbsp;<b>As:</b>&nbsp;!AZ</td></tr>\n\
<tr><th>CGI:</th><td>!&L</td></tr>\n\
<tr><th>OSU:</th><td>!&L</td></tr>\n\
<tr><th>Total:</th><td>!&L</td></tr>\n\
</table>\n\
</td></tr>\n\
\
</table>\n\
</td></tr>\n\
</table>\n";

   static char GzipFao [] =
"<p><table class=\"ctgry\">\n\
<tr><th class=\"ctttl\">Gzip Encoding!&?\r&nbsp;-&nbsp;DISABLED\r</th></tr>\n\
<tr><td>\n\
<table class=\"rghtrght\">\n\
<tr><th></th>\
<th class=\"sbttl\">Count</th>\
<th class=\"sbttl\">Of&nbsp;Total</th>\
<th class=\"sbttl\">Bytes&nbsp;In</th>\
<th class=\"sbttl\">Bytes&nbsp;Out</th>\
<th class=\"sbttl\">Ratio</th>\
</tr>\n\
<tr><th>Deflate:</th>\
<td>!&L</td>\
<td>!UL%</td>\
<td>!&,@UQ</td>\
<td>!&,@UQ</td>\
<td>!UL%</td>\
</tr>\n\
<tr><th>Inflate:</th>\
<td>!&L</td>\
<td>!UL%</td>\
<td>!&,@UQ</td>\
<td>!&,@UQ</td>\
<td>!UL%</td>\
</tr>\n\
<tr><th>Total&nbsp;Tx:</th>\
<td>!&L</td>\
<td>100%</td>\
<td>!&,@UQ</td>\
<td>!&,@UQ</td>\
<td>!UL%</td>\
</tr>\n\
</table>\n\
</td></tr>\n\
</table>\n";

   static char LookupFao [] =
"<p><table class=\"ctgry\">\n\
<tr><th class=\"ctttl\">Host Resolution</th></tr>\n\
<tr><td>\n\
<table class=\"rghtrght\">\n\
<tr><th></th>\
<th class=\"sbttl\">Literal</th>\
<th class=\"sbttl talft\" colspan=\"2\">DNS</th>\
<th class=\"sbttl\">Cache</th>\
<th class=\"sbttl\">Error</th>\
</tr>\n\
<tr><th>Address:</th>\
<td>!&L</td>\
<td></td>\
<td></td>\
<td></td>\
<td>!&L</td>\
</tr>\n\
<tr><th>Address-to-name:</th>\
<td></td>\
<td>!&L</td>\
<td class=\"talft\">(!UL%)</td>\
<td>!&L</td>\
<td>!&L</td>\
</tr>\n\
<tr><th>Name-to-address:</th>\
<td></td>\
<td>!&L</td>\
<td class=\"talft\">(!UL%)</td>\
<td>!&L</td>\
<td>!&L</td>\
</tr>\n\
</table>\n\
</td></tr>\n\
</table>\n";

   static char OtherFao [] =
"<p><table class=\"ctgry\">\n\
<tr><th class=\"ctttl\">Other</th></tr>\n\
<tr><td>\n\
<table class=\"rghtlft\">\n\
<tr><th>Statistics Zeroed:</th><td>!&L</td></tr>\n"
#if ODS_DIRECT
"!AZ"
#endif /* ODS_DIRECT */
#if ALIGN_FAULT
"<tr><th>Alignment Fault:</th><td>!&L</td></tr>\n"
#endif /* ALIGN_FAULT */
"<tr><th>Path Alert:</th><td>!&L</td></tr>\n\
<tr><th>NCS Conversion:</th>\
<td>!&L&nbsp;/&nbsp;!&L</td></tr>\n\
<tr><th>StreamLF File Conversion:</th>\
<td>!&L</td></tr>\n\
<tr><th>Errors Noticed:</th>\
<td>!&L</td><td>!&@!&@!AZ</td></tr>\n\
<tr><th>$HIBER spurious wake:</th>\
<td>!&L</td></tr>\n\
</table>\n\
</td></tr>\n\
</table>\n\
\
</div>\n\
</body>\n\
</html>\n";

   static char  SysPrvWarning[] = "<font color=\"#ff0000\">SYSPRV</font>";
   static char  PrivWarning[] = "<font color=\"#ff0000\">WARNING</font>";
   static char  PrivExpected[] = "as expected";

   static char  JpiPrcNam [16],
                JpiUserName [13],
                LastExitPid [16],
                LastExitStatus [16],
                LastExitTime [24];

   static unsigned short  SyiClusterNodes;

   static unsigned long  JpiAstCnt,
                         JpiBioCnt,
                         JpiBytCnt,
                         JpiCpuTime,
                         JpiDioCnt,
                         JpiEnqCnt,
                         JpiFilCnt,
                         JpiPageFlts,
                         JpiPagFilCnt,
                         JpiPid,
                         JpiPrcCnt,
                         JpiTqCnt,
                         JpiVirtPeak,
                         JpiWsSize,
                         JpiWsPeak,
                         Pid;

   static unsigned long  JpiAuthPriv [2],
                         JpiCurPriv [2],
                         JpiImagPriv [2];
   static int64  Time64,
                 ImageUpTime64,
                 JpiLoginTime64,
                 ProcessUpTime64;

   static $DESCRIPTOR (LastExitPidDsc, LastExitPid);
   static $DESCRIPTOR (LastExitStatusDsc, LastExitStatus);
   static $DESCRIPTOR (LastExitPidFaoDsc, "!8XL\0");
   static $DESCRIPTOR (LastExitStatusFaoDsc, "%X!XL\0");
   static $DESCRIPTOR (LastExitTimeDsc, LastExitTime);
   static $DESCRIPTOR (LastExitTimeFaoDsc, "!20%D\0");
   static VMS_ITEM_LIST3  JpiItems [] =
   {
      { sizeof(JpiAstCnt), JPI$_ASTCNT, &JpiAstCnt, 0 },
      { sizeof(JpiAuthPriv), JPI$_AUTHPRIV, &JpiAuthPriv, 0 },
      { sizeof(JpiBioCnt), JPI$_BIOCNT, &JpiBioCnt, 0 },
      { sizeof(JpiBytCnt), JPI$_BYTCNT, &JpiBytCnt, 0 },
      { sizeof(JpiCpuTime), JPI$_CPUTIM, &JpiCpuTime, 0 },
      { sizeof(JpiCurPriv), JPI$_CURPRIV, &JpiCurPriv, 0 },
      { sizeof(JpiDioCnt), JPI$_DIOCNT, &JpiDioCnt, 0 },
      { sizeof(JpiEnqCnt), JPI$_ENQCNT, &JpiEnqCnt, 0 },
      { sizeof(JpiFilCnt), JPI$_FILCNT, &JpiFilCnt, 0 },
      { sizeof(JpiImagPriv), JPI$_IMAGPRIV, &JpiImagPriv, 0 },
      { sizeof(JpiLoginTime64), JPI$_LOGINTIM, &JpiLoginTime64, 0 },
      { sizeof(JpiPageFlts), JPI$_PAGEFLTS, &JpiPageFlts, 0 },
      { sizeof(JpiPagFilCnt), JPI$_PAGFILCNT, &JpiPagFilCnt, 0 },
      { sizeof(JpiPid), JPI$_PID, &JpiPid, 0 },
      { sizeof(JpiPrcCnt), JPI$_PRCCNT, &JpiPrcCnt, 0 },
      { sizeof(JpiPrcNam), JPI$_PRCNAM, &JpiPrcNam, 0 },
      { sizeof(JpiTqCnt), JPI$_TQCNT, &JpiTqCnt, 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}
   };
   static VMS_ITEM_LIST3  SyiItem [] =
   {
     { sizeof(SyiClusterNodes), SYI$_CLUSTER_NODES, &SyiClusterNodes, 0 },
     { 0,0,0,0 }
   };

   int64  bytes64;
   int  cnt, idx, status, total,
        ConnectTotalCount,
        GzipDeflatePercent,
        GzipInflatePercent,
        MatchPercent,
        RequestHttpCount,
        StringTotal,
        TotalCount;
   unsigned int  bit;
   unsigned long  Remainder,
                  Seconds,
                  SubSeconds;
   unsigned long  *vecptr;
   unsigned long  FaoVector [180];
   char  *cptr, *sptr,
         *AuthPrivPtr,
         *CurPrivPtr,
         *ImagPrivPtr,
         *ClusterInstancePtr,
         *NodeInstancePtr;
   char  EnvNumber [16];
   IO_SB  IOsb;
   ODS_STRUCT  WasdRootOds;

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD__OTHER))
      WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER,
                 "AdminReportServerStats()");

   status = sys$getjpiw (EfnWait, &Pid, 0, &JpiItems, &IOsb, 0, 0);
   if (VMSok (status)) status = IOsb.Status;
   if (VMSnok (status))
   {
      rqptr->rqResponse.ErrorTextPtr = "sys$getjpiw()";
      ErrorVmsStatus (rqptr, status, FI_LI);
      AdminEnd (rqptr);
      return;
   }

   status = sys$getsyiw (EfnWait, 0, 0, &SyiItem, &IOsb, 0, 0);
   if (VMSok (status)) status = IOsb.Status;
   if (VMSnok (status))
   {
      rqptr->rqResponse.ErrorTextPtr = "sys$getsyiw()";
      ErrorVmsStatus (rqptr, status, FI_LI);
      AdminEnd (rqptr);
      return;
   }

   InstanceLockList (INSTANCE_NODE_SUPERVISOR, "<br>", &NodeInstancePtr);
   InstanceLockList (INSTANCE_CLUSTER, "<br>", &ClusterInstancePtr);

   JpiUserName[12] = '\0';
   for (cptr = JpiUserName; *cptr && *cptr != ' '; cptr++);
   *cptr = '\0';

   JpiPrcNam[15] = '\0';
   for (cptr = JpiPrcNam; *cptr && *cptr != ' '; cptr++);
   *cptr = '\0';

   sys$gettim (&Time64);
   ImageUpTime64 = HttpdStartTime64 - Time64;
   ProcessUpTime64 = JpiLoginTime64 - Time64;

   if ((JpiAuthPriv[0] & ~(PRV$M_NETMBX | PRV$M_TMPMBX)) || JpiAuthPriv[1])
      AuthPrivPtr = PrivWarning;
   else
      AuthPrivPtr = PrivExpected;

   if ((JpiImagPriv[0] & IMAG_PRIV_0_ALLOWED) ||
       (JpiImagPriv[1] & IMAG_PRIV_1_ALLOWED))
      ImagPrivPtr = PrivWarning;
   else
      ImagPrivPtr = PrivExpected;

   if (OperateWithSysPrv)
   {
      if ((JpiCurPriv[0] & ~(PRV$M_NETMBX | PRV$M_TMPMBX | PRV$M_SYSPRV)) ||
          JpiCurPriv[1])
         CurPrivPtr = PrivWarning;
      else
         CurPrivPtr = SysPrvWarning;
   }
   else
   if ((JpiCurPriv[0] & ~(PRV$M_NETMBX | PRV$M_TMPMBX)) || JpiCurPriv[1])
      CurPrivPtr = PrivWarning;
   else
      CurPrivPtr = PrivExpected;

   AdminPageTitle (rqptr, "Server Statistics");

   InstanceMutexLock (INSTANCE_MUTEX_HTTPD);

   ConnectTotalCount = acptr->ConnectCount[HTTP12];

   if (acptr->LastExitStatus)
      sys$fao (&LastExitStatusFaoDsc, 0, &LastExitStatusDsc, 
               acptr->LastExitStatus);
   else
      strzcpy (LastExitStatus, "<i>(none)</i>", sizeof(LastExitStatus));

   if (acptr->LastExitTime64)
      sys$fao (&LastExitTimeFaoDsc, 0, &LastExitTimeDsc, 
               &acptr->LastExitTime64);
   else
      strzcpy (LastExitTime, "<i>(none)</i>", sizeof(LastExitTime));

   if (acptr->LastExitPid)
      sys$fao (&LastExitPidFaoDsc, 0, &LastExitPidDsc,  acptr->LastExitPid);
   else
      strzcpy (LastExitPid, "<i>(none)</i>", sizeof(LastExitPid));

   /***************/
   /* environment */
   /***************/

   vecptr = FaoVector;

   if (InstanceEnvNumber > 1)
      sprintf (*vecptr++ = EnvNumber, "&nbsp;%d", InstanceEnvNumber);
   else
      *vecptr++ = "";

   *vecptr++ = SoftwareID;
   *vecptr++ = BuildInfo;
   if (cptr = SesolaVersion())
      *vecptr++ = cptr;
   else
      *vecptr++ = "<i>(none)</i>";

   if (Config.cfMisc.GzipResponseCompLevel)
   {
      *vecptr++ = "<tr><th>ZLIB:</th><td>!&@</td></tr>\n";
      if (VMSok(GzipFindImageStatus))
      {
         *vecptr++ = "!AZ V!AZ";
         *vecptr++ = GzipZlibNamePtr;
         *vecptr++ = GzipZlibVersionPtr;
      }
      else
      if (GzipFindImageStatus == RMS$_FNF)
         *vecptr++ = "%HTTPD-W-GZIP, shareable image not found";
      else
      {
         *vecptr++ = "!AZ %!&M";
         *vecptr++ = GzipZlibNamePtr;
         *vecptr++ = GzipFindImageStatus;
      }
   }
   else
      *vecptr++ = "";

   *vecptr++ = TcpIpAgentInfo;
   *vecptr++ = SysInfo.HwName;
   *vecptr++ = SysInfo.AvailCpuCnt;
   *vecptr++ = SysInfo.MemoryMBGB;
   *vecptr++ = SysInfo.Version;
   *vecptr++ = CommandLine;

   cptr = CONFIG_WASD_ROOT;
   OdsStructInit (&WasdRootOds, true);
   OdsParse (&WasdRootOds, cptr, strlen(cptr), NULL, 0,
             NAM$M_NOCONCEAL | NAM$M_SYNCHK, 0, 0);
   sptr = strstr (WasdRootOds.ExpFileName, ".][000000]");
   if (sptr) SET2(sptr,']\0');

   *vecptr++ = strchr(cptr,':') - cptr;
   *vecptr++ = cptr;
   *vecptr++ = WasdRootOds.ExpFileName;

   /*************/
   /* instances */
   /*************/

   *vecptr++ = NodeInstancePtr;
   if (SyiClusterNodes > 1)
      *vecptr++ = ClusterInstancePtr;
   else
      *vecptr++ = "<i>(n/a)</i>";

   if (HttpdGblSecPtr->InstancePassive)
      *vecptr++ = "Node instances are currently PASSIVE.";
   else
      *vecptr++ = "Node instances are currently ACTIVE.";

   /***********/
   /* process */
   /***********/

   *vecptr++ = (InstanceNodeCurrent <= 1);
   *vecptr++ = JpiPrcNam;
   *vecptr++ = ADMIN_REPORT_SHOW_PROCESS;
   *vecptr++ = JpiPid;
   if (DclPersonaServicesAvailable ||
       HttpdScriptAsUserName[0])
   {
      *vecptr++ = "&puser=!AZ";
      *vecptr++ = (unsigned long)JpiUserName;
   }
   else
      *vecptr++ = "";
   *vecptr++ = JpiPid;
   *vecptr++ = (unsigned long)JpiUserName;
   *vecptr++ = AuthPrivPtr;
   *vecptr++ = ImagPrivPtr;
   *vecptr++ = CurPrivPtr;
   *vecptr++ = LastExitStatus;
   *vecptr++ = LastExitTime;
   *vecptr++ = LastExitPid;
   *vecptr++ = &ImageUpTime64;
   *vecptr++ = &ProcessUpTime64;
   *vecptr++ = JpiCpuTime / 8640000;             /* CPU day */
   *vecptr++ = (JpiCpuTime % 8640000) / 360000;  /* CPU hour */
   *vecptr++ = (JpiCpuTime % 360000) / 6000;     /* CPU minute */
   *vecptr++ = (JpiCpuTime % 6000 ) / 100;       /* CPU second */
   *vecptr++ = JpiCpuTime % 100;                 /* CPU 10 milliseconds */
   *vecptr++ = JpiPageFlts;
   *vecptr++ = acptr->StartupCount;
   *vecptr++ = HttpdProcess.ModeName;
   *vecptr++ = JpiWsSize;
   *vecptr++ = JpiWsSize * 512 / 1048576,
   *vecptr++ = JpiWsPeak;
   *vecptr++ = JpiWsPeak * 512 / 1048576,
   *vecptr++ = JpiVirtPeak;
   *vecptr++ = JpiVirtPeak * 512 / 1048576,
   *vecptr++ = JpiAstCnt;
   *vecptr++ = HttpdProcess.AstLm;
   *vecptr++ = 100 - PercentOf32(JpiAstCnt,HttpdProcess.AstLm);
   *vecptr++ = JpiBioCnt;
   *vecptr++ = HttpdProcess.BioLm;
   *vecptr++ = 100 - PercentOf32(JpiBioCnt,HttpdProcess.BioLm);
   *vecptr++ = JpiBytCnt;
   *vecptr++ = HttpdProcess.BytLm;
   *vecptr++ = 100 - PercentOf32(JpiBytCnt,HttpdProcess.BytLm);
   *vecptr++ = JpiDioCnt;
   *vecptr++ = HttpdProcess.DioLm;
   *vecptr++ = 100 - PercentOf32(JpiDioCnt,HttpdProcess.DioLm);
   *vecptr++ = JpiEnqCnt;
   *vecptr++ = HttpdProcess.EnqLm;
   *vecptr++ = 100 - PercentOf32(JpiEnqCnt,HttpdProcess.EnqLm);
   *vecptr++ = JpiFilCnt;
   *vecptr++ = HttpdProcess.FilLm;
   *vecptr++ = 100 - PercentOf32(JpiFilCnt,HttpdProcess.FilLm);
   *vecptr++ = JpiPagFilCnt;
   *vecptr++ = HttpdProcess.PgFlQuo;
   *vecptr++ = (uint)JpiPagFilCnt * 512 / 1048576;
   *vecptr++ = (uint)HttpdProcess.PgFlQuo * 512 / 1048576;
   *vecptr++ = 100 - PercentOf32((uint)JpiPagFilCnt,(uint)HttpdProcess.PgFlQuo);
   if (DclScriptDetachProcess)
      *vecptr++ = "(detached)";
   else
   {
      *vecptr++ = "!&L&nbsp;/&nbsp;!&L&nbsp;(!UL%)";
      *vecptr++ = HttpdProcess.PrcLm - JpiPrcCnt;
      *vecptr++ = HttpdProcess.PrcLm;
      *vecptr++ = 100 - PercentOf32(HttpdProcess.PrcLm-JpiPrcCnt,
                                  HttpdProcess.PrcLm);
   }
   *vecptr++ = JpiTqCnt;
   *vecptr++ = HttpdProcess.TqLm;
   *vecptr++ = 100 - PercentOf32(JpiTqCnt,HttpdProcess.TqLm);

   if (HttpdProcess.SysInputFile)
   {
      *vecptr++ = "<a href=\"!AZ\">!AZ</a>";
      *vecptr++ = ADMIN_REPORT_PROCESS_INPUT;
   }
   *vecptr++ = HttpdProcess.SysInput;

   if (HttpdProcess.SysOutputFile)
   {
      *vecptr++ = "<a href=\"!AZ\">!AZ</a>";
      *vecptr++ = ADMIN_REPORT_PROCESS_OUTPUT_RAW;
   }
   *vecptr++ = HttpdProcess.SysOutput;

   InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);

   if (cptr = SysTrnLnm (WASTEE_LOGFILE))
   {
      *vecptr++ = "<tr><th>WAStee:</th><td colspan=\"3\">\
<font size=\"-1\"><a href=\"!AZ\">!AZ</a></font></td></tr>\n";
      *vecptr++ = ADMIN_REPORT_WASTEE;
      *vecptr++ = cptr;
   }
   else
      *vecptr++ = "";

   *vecptr++ = GblPageCount;
   *vecptr++ = GblSectionCount;
   *vecptr++ = GblPagePermCount;
   *vecptr++ = GblSectionPermCount;
   *vecptr++ = GblPageCount + GblPagePermCount;
   *vecptr++ = GblSectionCount + GblSectionPermCount;

   FaoCheck (sizeof(FaoVector), &FaoVector, vecptr, FI_LI);
   status = FaolToNet (rqptr, BeginPageFao, &FaoVector);
   if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);

   /************/
   /* services */
   /************/

   NetServiceReportStats (rqptr);

   /**************/
   /* statistics */
   /**************/

   InstanceMutexLock (INSTANCE_MUTEX_HTTPD);

   vecptr = FaoVector;

   /* connections */
   *vecptr++ = acptr->ConnectCount[HTTP12];
   *vecptr++ = acptr->CurrentConnected[HTTP12];
   *vecptr++ = acptr->ConnectPeak[HTTP12];
   *vecptr++ = acptr->ConnectTooBusyCount;
   *vecptr++ = acptr->ConnectCount[HTTP2];
   *vecptr++ = PercentOf32(acptr->ConnectCount[HTTP2],
                           acptr->ConnectCount[HTTP12]);
   *vecptr++ = acptr->CurrentConnected[HTTP2];
   *vecptr++ = PercentOf32(acptr->CurrentConnected[HTTP2],
                           acptr->CurrentConnected[HTTP12]);
   *vecptr++ = acptr->ConnectPeak[HTTP2];
   *vecptr++ = acptr->CurrentHttp1Persistent;
   *vecptr++ = acptr->ConnectPeakPersistent;
   *vecptr++ = acptr->ConnectIpv4Count;
   *vecptr++ = PercentOf32(acptr->ConnectIpv4Count,
                           acptr->ConnectCount[HTTP12]);
   *vecptr++ = acptr->ConnectIpv6Count;
   *vecptr++ = PercentOf32(acptr->ConnectIpv6Count,
                           acptr->ConnectCount[HTTP12]);
   *vecptr++ = acptr->ConnectSSLCount;
   *vecptr++ = PercentOf32(acptr->ConnectSSLCount,
                           acptr->ConnectCount[HTTP12]);

   *vecptr++ = &acptr->BytesRawRx64;
   *vecptr++ = &acptr->BytesRawTx64;
   *vecptr++ = acptr->NetReadErrorCount;
   *vecptr++ = acptr->NetWriteErrorCount;
   *vecptr++ = acptr->ConnectAcceptedCount;
   *vecptr++ = acptr->ConnectRejectedCount;

   /* requests */
   *vecptr++ = acptr->ProcessingTotalCount[HTTP12];
   *vecptr++ = acptr->CurrentProcessing[HTTP12];
   *vecptr++ = acptr->ProcessingPeak[HTTP12];
   *vecptr++ = acptr->ProcessingTooBusyCount;
   *vecptr++ = acptr->ProcessingTotalCount[HTTP2];
   *vecptr++ = PercentOf32(acptr->ProcessingTotalCount[HTTP2],
                           acptr->ProcessingTotalCount[HTTP12]);
   *vecptr++ = acptr->CurrentProcessing[HTTP2];
   *vecptr++ = PercentOf32(acptr->CurrentProcessing[HTTP2],
                           acptr->CurrentProcessing[HTTP12]);
   *vecptr++ = acptr->ProcessingPeak[HTTP2];

   for (total = cnt = 0; cnt <= InstanceNodeCurrent; cnt++)
      total += acptr->CurrentWebSockets[cnt];
   *vecptr++ = total;
   StringTotal = total;
   *vecptr++ = acptr->WebSocketCount;
   for (total = cnt = 0; cnt <= InstanceNodeCurrent; cnt++)
      total += acptr->CurrentWebSocketsRaw[cnt];
   *vecptr++ = total;
   *vecptr++ = PercentOf32(StringTotal,total);
   *vecptr++ = acptr->WebSocketRawCount;
   *vecptr++ = PercentOf32(acptr->WebSocketCount,acptr->WebSocketRawCount);

   *vecptr++ = acptr->RequestSSLCount;
   *vecptr++ = PercentOf32(acptr->RequestSSLCount,
                           acptr->ProcessingTotalCount[HTTP12]);

   for (total = cnt = 0; cnt <= InstanceNodeCurrent; cnt++)
      total += acptr->CurrentThrottleQueued[cnt];
   *vecptr++ = total;
   for (total = cnt = 0; cnt <= InstanceNodeCurrent; cnt++)
      total += acptr->CurrentThrottleProcessing[cnt];
   *vecptr++ = total;
   *vecptr++ = acptr->ThrottleBusyMetric;
   *vecptr++ = acptr->RequestPersistentCount;
   *vecptr++ = acptr->RequestPersistentMax;
   *vecptr++ = acptr->PipelineRequestCount;
   *vecptr++ = acptr->PipelineRequestMax;

   /* force it to the left because of the following 'duration' width */
   if (ConnectTotalCount < 10)
      *vecptr++ = 36;
   else
   if (ConnectTotalCount < 100)
      *vecptr++ = 30;
   else
   if (ConnectTotalCount < 1000)
      *vecptr++ = 24;
   else
   if (ConnectTotalCount < 10000)
      *vecptr++ = 18;
   else
   if (ConnectTotalCount < 100000)
      *vecptr++ = 12;
   else
   if (ConnectTotalCount < 1000000)
      *vecptr++ = 6;
   else
      *vecptr++ = 0;
   *vecptr++ = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";

   /* HTTP versions */
   RequestHttpCount = acptr->RequestHttp2Count +
                      acptr->RequestHttp11Count +
                      acptr->RequestHttp10Count +
                      acptr->RequestHttp09Count;
   *vecptr++ = acptr->RequestHttp2Count;
   *vecptr++ = PercentOf32 (acptr->RequestHttp2Count, RequestHttpCount);
   *vecptr++ = acptr->RequestHttp11Count;
   *vecptr++ = PercentOf32 (acptr->RequestHttp11Count, RequestHttpCount);
   *vecptr++ = acptr->RequestHttp10Count;
   *vecptr++ = PercentOf32 (acptr->RequestHttp10Count, RequestHttpCount);
   *vecptr++ = acptr->RequestHttp09Count;
   *vecptr++ = PercentOf32 (acptr->RequestHttp09Count, RequestHttpCount);

   /* HTTP methods */
   *vecptr++ = acptr->MethodConnectCount;
   *vecptr++ = acptr->MethodWebDavCopyCount;
   *vecptr++ = acptr->MethodDeleteCount;
   *vecptr++ = acptr->MethodGetCount;
   *vecptr++ = acptr->MethodHeadCount;
   *vecptr++ = acptr->MethodWebDavLockCount;
   *vecptr++ = acptr->MethodWebDavMkColCount;
   *vecptr++ = acptr->MethodWebDavMoveCount;
   *vecptr++ = acptr->MethodOptionsCount;
   *vecptr++ = acptr->MethodPostCount;
   *vecptr++ = acptr->MethodWebDavPropFindCount;
   *vecptr++ = acptr->MethodWebDavPropPatchCount;
   *vecptr++ = acptr->MethodPutCount;
   *vecptr++ = acptr->MethodTraceCount;
   *vecptr++ = acptr->MethodWebDavUnLockCount;
   *vecptr++ = acptr->MethodExtensionCount;
   *vecptr++ = acptr->MethodSshCount;

   /* responses */

   *vecptr++ = acptr->ResponseStatusCodeGroup[1];
   *vecptr++ = acptr->ResponseStatusCodeGroup[2];
   *vecptr++ = acptr->ResponseStatusCodeGroup[3];
   *vecptr++ = acptr->ResponseStatusCodeGroup[4];
   *vecptr++ = acptr->ResponseStatusCodeGroup[5];

   /* see ResponseHttpStatusCode() for making sense of this array */

#define RHSI(n) RequestHttpStatusIndex(n)

   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(100)];
   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(200)];
   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(300)];
   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(400)];
   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(500)];

   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(101)];
   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(201)];
   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(301)];
   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(401)];
   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(501)];

   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(102)];
   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(202)];
   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(302)];
   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(402)];
   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(502)];

   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(203)];
   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(303)];
   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(403)];
   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(503)];

   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(204)];
   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(304)];
   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(404)];
   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(504)];

   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(205)];
   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(305)];
   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(405)];
   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(505)];

   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(206)];
   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(306)];
   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(406)];

   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(207)];
   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(307)];
   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(407)];
   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(507)];

   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(408)];
   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(409)];
   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(410)];
   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(411)];
   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(412)];
   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(413)];
   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(414)];
   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(415)];
   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(416)];
   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(417)];
   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(418)];
   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(421)];
   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(422)];
   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(423)];
   *vecptr++ = acptr->ResponseStatusCodeCount[RHSI(424)];

   *vecptr++ = acptr->ResponseStatusCodeGroup[0];  /* 000 */
   *vecptr++ = acptr->ResponseStatusCodeCount[0];  /* ??? */

#undef RHSI

   /* response duration */
   /* without this kludge we get %CC-W-ALIGNCONFLICT */
   bytes64 = acptr->ResponseDurationMin64[HTTP12];
   *vecptr++ = DurationString (rqptr, &bytes64);
   bytes64 = acptr->ResponseDurationMax64[HTTP12];
   *vecptr++ = DurationString (rqptr, &bytes64);
   bytes64 = acptr->ResponseDuration64[HTTP12];
   *vecptr++ = AverageDurationString (rqptr, &bytes64,
                                      acptr->ResponseDurationCount[HTTP12]);
   bytes64 = acptr->ResponseDuration64[HTTP12];
   *vecptr++ = DurationString (rqptr, &bytes64);

   /* bytes per second */
   *vecptr++ = acptr->BytesPerSecondMin[HTTP12];
   *vecptr++ = &acptr->BytesPerSecondMinBytes64[HTTP12];
   bytes64 = acptr->BytesPerSecondMinDuration64[HTTP12];
   *vecptr++ = DurationString (rqptr, &bytes64);

   *vecptr++ = acptr->BytesPerSecondMax[HTTP12];
   *vecptr++ = &acptr->BytesPerSecondMaxBytes64[HTTP12];
   bytes64 = acptr->BytesPerSecondMaxDuration64[HTTP12];
   *vecptr++ = DurationString (rqptr, &bytes64);

   *vecptr++ = acptr->BytesPerSecondAve[HTTP12];
   *vecptr++ = (ulong)((float)acptr->BytesPerSecondRawTotal64[HTTP12] /
                       (float)(acptr->RequestHttp2Count +
                               acptr->RequestHttp11Count +
                               acptr->RequestHttp10Count));
   bytes64 = acptr->ResponseDuration64[HTTP12];
   *vecptr++ = DurationString (rqptr, &bytes64);

   /* modules */
   *vecptr++ = acptr->DoServerAdminCount;
   *vecptr++ = acptr->CacheLoadCount;
   *vecptr++ = acptr->CacheHitCount;
   *vecptr++ = acptr->CacheHitNotModifiedCount;
   *vecptr++ = acptr->DoDirectoryCount;
   *vecptr++ = acptr->DoFileCount;
   *vecptr++ = acptr->DoFileNotModifiedCount;
   *vecptr++ = acptr->DoPutCount;
   *vecptr++ = acptr->DoProxyCount;
   *vecptr++ = acptr->DoSsiCount;
   *vecptr++ = acptr->DoUpdateCount;
   *vecptr++ = acptr->DoWebDavCount;
   *vecptr++ = acptr->DoWebSockCount;
   *vecptr++ = acptr->DoNoModuleCount;
   if (HttpdScriptAsUserName[0])
      *vecptr++ = HttpdScriptAsUserName;
   else
      *vecptr++ = HttpdProcess.UserName;
   *vecptr++ = acptr->DoScriptCount;
   *vecptr++ = acptr->DoCgiPlusScriptCount;
   *vecptr++ = acptr->DclCgiPlusReusedCount;
   *vecptr++ = acptr->DoRteScriptCount;
   *vecptr++ = acptr->DclRteReusedCount;
   *vecptr++ = acptr->DoAutoScriptCount;
   *vecptr++ = acptr->DoDclCommandCount;
   *vecptr++ = acptr->DclProctorCount;
   *vecptr++ = acptr->DclWebSocketCount;
   *vecptr++ = acptr->DclCrePrcCount;
   *vecptr++ = PersonaMacro;
   *vecptr++ = acptr->DclCrePrcPersonaCount;
   *vecptr++ = acptr->DclCrePrcPersonaDefaultCount;
   *vecptr++ = acptr->DclCrePrcPersonaInvUserCount;
   *vecptr++ = acptr->DclCrePrcPersonaPrvUserCount;
   *vecptr++ = acptr->DclForceXCount;
   *vecptr++ = acptr->DclDelPrcCount;
   if (HttpdScriptAsUserName[0])
      *vecptr++ = HttpdScriptAsUserName;
   else
      *vecptr++ = HttpdProcess.UserName;
   *vecptr++ = acptr->DoDECnetCgiCount;
   *vecptr++ = acptr->DoDECnetOsuCount;
   *vecptr++ = acptr->DoDECnetCount;

   InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);

   FaoCheck (sizeof(FaoVector), &FaoVector, vecptr, FI_LI);
   status = FaolToNet (rqptr, StatisticsFao, &FaoVector);
   if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);

   InstanceMutexLock (INSTANCE_MUTEX_HTTPD);

   if (Config.cfMisc.GzipResponseCompLevel)
   {
      /* GZIP compression (if enabled) */
      vecptr = FaoVector;

      *vecptr++ = GzipAvailable;
      *vecptr++ = acptr->GzipDeflateCount;
      *vecptr++ = PercentOf32 (acptr->GzipDeflateCount,
                               acptr->ProcessingTotalCount[HTTP12]);
      *vecptr++ = &acptr->GzipDeflateBytesIn64;
      *vecptr++ = &acptr->GzipDeflateBytesOut64;
      *vecptr++ = PercentOf64 (&acptr->GzipDeflateBytesOut64,
                               &acptr->GzipDeflateBytesIn64);

      *vecptr++ = acptr->GzipInflateCount;
      *vecptr++ = PercentOf32 (acptr->GzipInflateCount,
                               acptr->ProcessingTotalCount[HTTP12]);
      *vecptr++ = &acptr->GzipInflateBytesIn64;
      *vecptr++ = &acptr->GzipInflateBytesOut64;
      *vecptr++ = PercentOf64 (&acptr->GzipInflateBytesIn64,
                               &acptr->GzipInflateBytesOut64);

      bytes64 = acptr->BytesRawTx64[HTTP12] +
                (acptr->GzipDeflateBytesIn64 -
                 acptr->GzipDeflateBytesOut64);

      *vecptr++ = acptr->ProcessingTotalCount[HTTP12];
      *vecptr++ = &bytes64;
      *vecptr++ = &acptr->BytesRawTx64[HTTP12];
      *vecptr++ = PercentOf64 (&acptr->BytesRawTx64[HTTP12], &bytes64);

      FaoCheck (sizeof(FaoVector), &FaoVector, vecptr, FI_LI);
      status = FaolToNet (rqptr, GzipFao, &FaoVector);
      if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);
   }

   /* lookup */
   vecptr = FaoVector;

   *vecptr++ = acptr->LookupLiteralCount;
   *vecptr++ = acptr->LookupLiteralErrorCount;
   *vecptr++ = acptr->LookupDnsAddressCount;
   *vecptr++ = PercentOf32 (acptr->LookupDnsAddressCount,
                          acptr->LookupDnsAddressCount +
                          acptr->LookupCacheAddressCount);
   *vecptr++ = acptr->LookupCacheAddressCount;
   *vecptr++ = acptr->LookupDnsAddressErrorCount;
   *vecptr++ = acptr->LookupDnsNameCount;
   *vecptr++ = PercentOf32 (acptr->LookupDnsNameCount,
                          acptr->LookupDnsNameCount +
                          acptr->LookupCacheNameCount);
   *vecptr++ = acptr->LookupCacheNameCount;
   *vecptr++ = acptr->LookupDnsNameErrorCount;

   FaoCheck (sizeof(FaoVector), &FaoVector, vecptr, FI_LI);
   status = FaolToNet (rqptr, LookupFao, &FaoVector);
   if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);

   /* other */
   vecptr = FaoVector;

   *vecptr++ = acptr->ZeroedCount;
#if ODS_DIRECT
   *vecptr++ = OdsDirectReport (rqptr);
#endif /* ODS_DIRECT */
#if ALIGN_FAULT
   *vecptr++ = HttpdAlignFaultCount;
#endif /* ALIGN_FAULT */
   *vecptr++ = acptr->PathAlertCount;
   *vecptr++ = acptr->NcsCount;
   *vecptr++ = acptr->NcsConvertCount;
   *vecptr++ = acptr->StreamLfConversionCount;
   *vecptr++ = acptr->ErrorsNoticedCount;

   if (acptr->ErrorsNoticedCount)
   {
      *vecptr++ = "(!20%D";
      *vecptr++ = &acptr->ErrorsNoticedTime64;
      if (HttpdProcess.SysOutputFile)
      {
         *vecptr++ = ", see <a href=\"!AZ\">Log</a>";
         *vecptr++ = ADMIN_REPORT_PROCESS_OUTPUT_RAW;
      }
      else
         *vecptr++ = "";
      *vecptr++ = ")";
   }
   else
   {
      *vecptr++ = "";
      *vecptr++ = "";
      *vecptr++ = "";
   }

   *vecptr++ = acptr->SpuriousWakeCount;

   InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);

   FaoCheck (sizeof(FaoVector), &FaoVector, vecptr, FI_LI);
   status = FaolToNet (rqptr, OtherFao, &FaoVector);
   if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);

   VmFree (NodeInstancePtr, FI_LI);
   VmFree (ClusterInstancePtr, FI_LI);

   rqptr->rqResponse.PreExpired = PRE_EXPIRE_ADMIN;
   ResponseHeader200 (rqptr, "text/html", &rqptr->NetWriteBufferDsc);

   AdminEnd (rqptr);
}

/*****************************************************************************/
/*
Get the file module to output the server process log.
*/

void AdminServerInput (REQUEST_STRUCT *rqptr)

{
   int  status;

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD__OTHER))
      WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER, "AdminServerInput()");

   if (!HttpdProcess.SysInputFile)
   {
      rqptr->rqResponse.HttpStatus = 500;
      ErrorGeneral (rqptr, HttpdProcess.SysInput, FI_LI);
      AdminEnd (rqptr);
      return;
   }

   rqptr->rqResponse.PreExpired = PRE_EXPIRE_ADMIN;
   ResponseHeader200 (rqptr, "text/plain", NULL);

   FileSetCacheAllowed (rqptr, false);
   FileSetAuthorizePath (rqptr, false);

   FileBegin (rqptr, &AdminEnd, NULL, NULL,
              HttpdProcess.SysInput, "text/plain");
}

/*****************************************************************************/
/*
Get the file module to output the server process log.
*/

void AdminServerOutputRaw (REQUEST_STRUCT *rqptr)

{
   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD__OTHER))
      WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER, "AdminServerOutputRaw()");

   if (!HttpdProcess.SysOutputFile)
   {
      rqptr->rqResponse.HttpStatus = 500;
      ErrorGeneral (rqptr, HttpdProcess.SysOutput, FI_LI);
      AdminEnd (rqptr);
      return;
   }

   /* Q&D flush SYS$OUTPUT */
   fsync (STDOUT_FILENO);

   rqptr->rqResponse.PreExpired = PRE_EXPIRE_ADMIN;
   ResponseHeader200 (rqptr, "text/plain", NULL);

   FileSetCacheAllowed (rqptr, false);
   FileSetAuthorizePath (rqptr, false);

   FileBegin (rqptr, &AdminEnd, NULL, NULL,
              HttpdProcess.SysOutput, "text/plain");
}

/*****************************************************************************/
/*
List the process logs (directory index) or output a specific log file.
*/

void AdminServerOutputList (REQUEST_STRUCT *rqptr)

{
   int  sizeofList = sizeof(ADMIN_REPORT_PROCESS_OUTPUT_LIST)-1;
   char  *cptr, *pinptr, *sptr, *zptr;

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD__OTHER))
      WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER, "AdminServerOutputList()");

   /* Q&D flush SYS$OUTPUT */
   fsync (STDOUT_FILENO);

   rqptr->rqResponse.PreExpired = PRE_EXPIRE_ADMIN;

   zptr = (sptr = rqptr->RequestMappedFile) +
          sizeof(rqptr->RequestMappedFile)-1;
   for (cptr = "WASD_SERVER_LOGS:"; *cptr; *sptr++ = *cptr++);
   *sptr = '\0';

   for (cptr = pinptr = rqptr->rqHeader.PathInfoPtr; *cptr; cptr++);
   if ((cptr - pinptr) > sizeofList)
   {
      FileSetCacheAllowed (rqptr, false);
      FileSetAuthorizePath (rqptr, false);

      for (cptr = pinptr + sizeofList; *cptr && sptr < zptr; *sptr++ = *cptr++);
      *sptr = '\0';

      FileBegin (rqptr, &AdminEnd, NULL, NULL,
                 rqptr->RequestMappedFile, "text/plain");
   }
   else
      DirBegin (rqptr, &AdminEnd, rqptr->RequestMappedFile,
                "httpd=index&these=*.log&delimit=footer&readme=no", "", false);
}

/*****************************************************************************/
/*
From the Server Admin menu generate a report of errors noticed.
*/ 

void AdminServerOutputNoticed (REQUEST_STRUCT *rqptr)

{
   static char ReportBeginFao [] =
"<p><table class=\"ctgry\">\n\
<tr><td class=\"pretxt\" style=\"padding:1em;\">";

   static char ReportEndFao [] =
"</td></tr>\n\
</table>\n";

   /*********/
   /* begin */
   /*********/

   if (WATCH_MODULE(WATCH_MOD_NET))
      WatchThis (WATCHALL, WATCH_MOD_NET, "AdminServerOutputNoticed()");

   rqptr->rqResponse.PreExpired = PRE_EXPIRE_ADMIN;
   ResponseHeader200 (rqptr, "text/plain", NULL);

   AdminServerOutputNoticed2 (rqptr);

   AdminEnd (rqptr);
}

/*****************************************************************************/
/*
Generate a list of errors noticed by opening and reading relevant records from
the current operator log.  Relies on the $NOTE rule being enabled.
*/

void AdminServerOutputNoticed2 (REQUEST_STRUCT *rqptr)

{
   int  count, status;
   char  *sptr;
   char  buf [1024];
   ODS_STRUCT  OutputOds;
   ODS_STRUCT  *odsptr = &OutputOds;

   /*********/
   /* begin */
   /*********/

   if (WATCH_MODULE(WATCH_MOD_NET))
      WatchThis (WATCHALL, WATCH_MOD_NET, "AdminServerOutputNoticed2()");

   if (!HttpdProcess.SysOutputFile)
   {
      FaoToNet (rqptr, "!&S\n", SS$_NOSUCHFILE);
      return;
   }

   /* Q&D flush SYS$OUTPUT */
   fsync (STDOUT_FILENO);

   OdsStructInit (odsptr, true);
   status = OdsOpenShrUpdReadOnly (odsptr, HttpdProcess.SysOutput, false, true);
   if (VMSnok (status))
   {
      FaoToNet (rqptr, "!&S\n", status);
      return;
   }

   count = 0;

   for (;;)
   {
      odsptr->Rab.rab$l_ubf = buf;
      odsptr->Rab.rab$w_usz = sizeof(buf) - 1;
      status = sys$get (&odsptr->Rab, 0, 0);
      if (VMSnok (status)) break;
      buf[odsptr->Rab.rab$w_rsz++] = '\n';
      buf[odsptr->Rab.rab$w_rsz] = '\0';
      if (!MATCH15 (buf, "%HTTPD-W-ALERT,") &&
          !MATCH17 (buf, "%HTTPD-W-NOTICED,")) continue;
      for (;;)
      {
         if (count++) NetWriteBuffered (rqptr, NULL, "8< snip 8<\n", 11);
         NetWriteBuffered (rqptr, NULL, buf, odsptr->Rab.rab$w_rsz);
         for (;;)
         {
            /* all records associated with this notice */
            odsptr->Rab.rab$l_ubf = buf;
            odsptr->Rab.rab$w_usz = sizeof(buf) - 1;
            status = sys$get (&odsptr->Rab, 0, 0);
            if (VMSnok (status)) break;
            /* if empty record */
            if (!odsptr->Rab.rab$w_rsz) continue;
            buf[odsptr->Rab.rab$w_rsz++] = '\n';
            /* if start of new message */
            if (buf[0] == '%' && !MATCH15 (buf, "%HTTPD-W-ALERT,")) break;
            NetWriteBuffered (rqptr, NULL, buf, odsptr->Rab.rab$w_rsz);
         }
         if (VMSnok (status)) break;
         if (!MATCH15 (buf, "%HTTPD-W-ALERT,") &&
             !MATCH17 (buf, "%HTTPD-W-NOTICED,")) break;
      }
      if (VMSnok (status)) break;
   }

   sys$close (&odsptr->Fab, 0, 0);

   if (count && status == RMS$_EOF) status = SS$_NORMAL;
   if (VMSnok (status))
   {
      FaoToNet (rqptr, "!&S\n", status);
      return;
   }
}

/*****************************************************************************/
/*
From the Server Admin menu generate a report sans NOTICED and REJECT records.
*/ 

void AdminServerOutputFilter (REQUEST_STRUCT *rqptr)

{
   /*********/
   /* begin */
   /*********/

   if (WATCH_MODULE(WATCH_MOD_NET))
      WatchThis (WATCHALL, WATCH_MOD_NET, "AdminServerOutputFilter()");

   rqptr->rqResponse.PreExpired = PRE_EXPIRE_ADMIN;
   ResponseHeader200 (rqptr, "text/plain", NULL);

   AdminServerOutputFilter2 (rqptr);

   AdminEnd (rqptr);
}

/*****************************************************************************/
/*
Generate an expurgated list of records by opening and reading relevant records
from the current operator log.  Relies on the $NOTE rule being enabled.
*/

void AdminServerOutputFilter2 (REQUEST_STRUCT *rqptr)

{
   static char ReportEmptyFao [] = "<i>empty</i>\n";

   int  count, status;
   char  *sptr;
   char  buf [1024];
   ODS_STRUCT  OutputOds;
   ODS_STRUCT  *odsptr = &OutputOds;

   /*********/
   /* begin */
   /*********/

   if (WATCH_MODULE(WATCH_MOD_NET))
      WatchThis (WATCHALL, WATCH_MOD_NET, "AdminServerOutputFilter2()");

   if (!HttpdProcess.SysOutputFile)
   {
      FaoToNet (rqptr, "!&S\n", SS$_NOSUCHFILE);
      return;
   }

   /* Q&D flush SYS$OUTPUT */
   fsync (STDOUT_FILENO);

   OdsStructInit (odsptr, true);
   status = OdsOpenShrUpdReadOnly (odsptr, HttpdProcess.SysOutput, false, true);
   if (VMSnok (status))
   {
      FaoToNet (rqptr, "!&S\n", status);
      return;
   }

   count = 0;

   for (;;)
   {
      odsptr->Rab.rab$l_ubf = buf;
      odsptr->Rab.rab$w_usz = sizeof(buf) - 1;
      status = sys$get (&odsptr->Rab, 0, 0);
      if (VMSnok (status)) break;
      buf[odsptr->Rab.rab$w_rsz++] = '\n';
      buf[odsptr->Rab.rab$w_rsz] = '\0';
      if (MATCH16 (buf, "%HTTPD-I-REJECT,") ||
          MATCH16 (buf, "%HTTPD-I-ACCEPT,"))
      {
         if (!count++) NetWriteBuffered (rqptr, NULL, "8< snip 8<\n", 11);
         continue;
      }
      if (MATCH15 (buf, "%HTTPD-W-ALERT,") ||
          MATCH17 (buf, "%HTTPD-W-NOTICED,"))
      {
         for (;;)
         {
            if (!count++) NetWriteBuffered (rqptr, NULL, "8< snip 8<\n", 11);
            for (;;)
            {
               /* all records associated with this notice */
               odsptr->Rab.rab$l_ubf = buf;
               odsptr->Rab.rab$w_usz = sizeof(buf) - 1;
               status = sys$get (&odsptr->Rab, 0, 0);
               if (VMSnok (status)) break;
               /* if start of new message */
               if (buf[0] == '%') break;
            }
            if (VMSnok (status)) break;
            if (!MATCH15 (buf, "%HTTPD-W-ALERT,") &&
                !MATCH17 (buf, "%HTTPD-W-NOTICED,")) break;
         }
         if (VMSnok (status)) break;
         continue;
      }
      count = 0;
      NetWriteBuffered (rqptr, NULL, buf, odsptr->Rab.rab$w_rsz);
   }

   sys$close (&odsptr->Fab, 0, 0);

   if (status == RMS$_EOF) status = SS$_NORMAL;
   if (VMSnok (status))
   {
      FaoToNet (rqptr, "!&S\n", status);
      return;
   }
}

/*****************************************************************************/
/*
See WASD_ROOT:[SRC.UTILS]WASTEE.C for background.
*/

void AdminReportWAStee (REQUEST_STRUCT *rqptr)

{
   int  len;
   char  *sptr;

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD__OTHER))
      WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER, "AdminReportWAStee()");

   if (!(sptr = SysTrnLnm (WASTEE_LOGFILE)))
   {
      rqptr->rqResponse.HttpStatus = 500;
      ErrorGeneral (rqptr, "WASTEE_LOGFILE", FI_LI);
      AdminEnd (rqptr);
      return;
   }

   rqptr->rqResponse.PreExpired = PRE_EXPIRE_ADMIN;
   ResponseHeader200 (rqptr, "text/plain", NULL);

   FileSetCacheAllowed (rqptr, false);
   FileSetAuthorizePath (rqptr, false);

   FileBegin (rqptr, &AdminEnd, NULL, NULL, sptr, "text/plain");
}

/*****************************************************************************/
/*
Provide administration menu/report page titles.  Will accept a small number of
variable arguments to provide text following the titles themselves.
*/ 

int AdminPageTitle
(
REQUEST_STRUCT *rqptr,
char *Title,
...
)
{
   static char PageTitleFao [] =
"!AZ\
<html>\n\
<head>\n\
!AZ\
!AZ\
!&@\
<title>WASD !AZ ... !AZ</title>\n\
</head>\n\
!AZ\n\
<div class=\"wasd\">\n\
<h2><nobr>WASD !AZ</nobr></h2>\n\
<h3>!AZ!&@</h3>\n\
!20&W\n\
!&@";

   int  argcnt, status;
   unsigned long  *vecptr;
   unsigned long  FaoVector [32+16];
   va_list  argptr;

   /*********/
   /* begin */
   /*********/

   va_count (argcnt);

   if (WATCHMOD (rqptr, WATCH_MOD__OTHER))
      WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER,
                 "AdminPageTitle() !UL !&Z", argcnt, Title);

   if (argcnt > 32+4)
   {
      ErrorNoticed (rqptr, SS$_OVRMAXARG, NULL, FI_LI);
      return (SS$_OVRMAXARG);
   }

   vecptr = FaoVector;

   *vecptr++ = WASD_DOCTYPE;
   *vecptr++ = HtmlMetaInfo (rqptr, NULL);
   *vecptr++ = AdminWasdCss ();
   if (rqptr->rqPathSet.StyleSheetPtr)
   {
      *vecptr++ = "<link rel=\"stylesheet\" type=\"text/css\" href=\"!AZ\">\n";
      *vecptr++ = rqptr->rqPathSet.StyleSheetPtr;
   }
   else
      *vecptr++ = "";
   *vecptr++ = ServerHostPort;
   *vecptr++ = Title;
   *vecptr++ = ADMIN_BODY_TAG;
   *vecptr++ = ServerHostPort;
   *vecptr++ = Title;
   if (InstanceNodeCurrent > 1)
   {
      if (rqptr->ServicePtr->AdminService)
         *vecptr++ = " &nbsp;-&nbsp; !AZ";
      else
         *vecptr++ = " &nbsp;&nbsp; (!AZ)";
      *vecptr++ = HttpdProcess.PrcNam;
   }
   else
      *vecptr++ = "";
   *vecptr++ = &rqptr->rqTime.BeginTime64;

   if (argcnt > 2)
   {
      va_start (argptr, Title);
      for (argcnt -= 2; argcnt; argcnt--)
         *vecptr++ = va_arg (argptr, unsigned long);
      va_end (argptr);
   }
   else
      *vecptr++ = "";

   FaoCheck (sizeof(FaoVector), &FaoVector, vecptr, FI_LI);
   status = FaolToNet (rqptr, PageTitleFao, &FaoVector);
   if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);

   return (status);
}

/*****************************************************************************/
/*
Provide a list of problems reported during a meta-config load in standard
admininistration menu/report format.
*/
 
void AdminMetaConReport
(
REQUEST_STRUCT *rqptr,
META_CONFIG *mcptr,
META_CONFIG *MetaConStartupPtr
)
{
   static char  ReportFao [] =
"<p><table class=\"ctgry\">\n\
<tr><th class=\"ctttl\">\
&nbsp;&nbsp;!UL Informational,&nbsp; !UL Warning,&nbsp; \
!&?<span style=\"color:#ff0000\">\r\r!-!UL Error!%s&nbsp;!-!&?</span>\r\r \\
!&?At Startup\rDuring Load\r&nbsp;&nbsp;</th></tr>\n\
<tr><td><pre>!AZ</pre></td></tr>\n\
</table>\n";

   int  status;
   unsigned long  *vecptr;
   unsigned long  FaoVector [16];

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD__OTHER))
      WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER, "AdminMetaConReport()");

   if (!mcptr->LoadReport.ItemCount) return;

   vecptr = FaoVector;
   *vecptr++ = mcptr->LoadReport.InformCount;
   *vecptr++ = mcptr->LoadReport.WarningCount;
   *vecptr++ = mcptr->LoadReport.ErrorCount;
   *vecptr++ = (mcptr == MetaConStartupPtr);
   *vecptr++ = mcptr->LoadReport.TextPtr;

   FaoCheck (sizeof(FaoVector), &FaoVector, vecptr, FI_LI);
   status = FaolToNet (rqptr, ReportFao, &FaoVector);
   if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);
}

/*****************************************************************************/
/*
Provide a description of the source of this configuration in an administration
standard menu/report format.
*/
 
void AdminMetaConSource
(
REQUEST_STRUCT *rqptr,
META_CONFIG *mcptr,
META_CONFIG *MetaConStartupPtr
)
{
   static char  SourceFao [] =
"<p><table class=\"ctgry\">\n\
<tr><th class=\"ctttl\">Source: &quot;!&?Server\rFile\r&quot;</th></tr>\n\
<tr><td>\n\
<table class=\"rghtlft\">\n\
<tr><th>File:</th><td>!AZ</td>!&@</tr>\n\
<tr><th>!AZ</th><td>!20&W</td></tr>\n\
!&@\
!&@\
</table>\n\
</td></tr>\n\
</table>\n";

   int  status;
   unsigned long  *vecptr;
   unsigned long  FaoVector [16];
   char  *cptr;

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD__OTHER))
      WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER, "AdminMetaConSource()");

   vecptr = FaoVector;
   *vecptr++ = (mcptr == MetaConStartupPtr);
   *vecptr++ = mcptr->LoadReport.FileName;
   if (mcptr->LnmCount > 1)
      *vecptr++ = "<th>(search list)</th>";
   else
   {
      *vecptr++ =
"<th style=\"padding-left:1em;\">\
<a class=\"abttn bttn400\" href=\"!AZ\">View</a></th>\
<th><a class=\"abttn bttn400\" href=\"!AZ?do=edit\">Edit</a></th></tr>";
      *vecptr++ = cptr = rqptr->rqHeader.PathInfoPtr;
      *vecptr++ = cptr;
   }

   if (mcptr == MetaConStartupPtr)
   {
      *vecptr++ = "Loaded:";
      *vecptr++ = &mcptr->LoadReport.LoadTime64;
   }
   else
   {
      *vecptr++ = "Revised:";
      *vecptr++ = &mcptr->LoadReport.FileTime64;
   }

   if (InConfigStartup (true))
   {
      *vecptr++ =
"<tr><th>From:</th><td>!AZ</td>\n\
<th style=\"padding-left:1em;\">\
<a class=\"abttn bttn400\" href=\"!AZ\">View</a></th>\
<th><a class=\"abttn bttn400\" href=\"!AZ?do=edit\">Edit</a></th></tr>\n\
<tr><td colspan=\"5\" style=\"color:#ff0000; font-weight:bold; \
text-align:center\">\
Configuration derived from inline directives and file(s).</b><br>\
CAUTION: Using this interface may overwrite that!!\
</td></tr>\n";
      *vecptr++ = InConfigFileName;
      *vecptr++ = ADMIN_REPORT_INLINE;
      *vecptr++ = ADMIN_REPORT_INLINE;
      InConfigStartup (false);
   }
   else
      *vecptr++ = "";

   if (mcptr->IncludeFile)
      *vecptr++ =
"<tr><td colspan=\"5\" style=\"color:#ff0000; font-weight:bold; \
text-align:center\">\
Configuration derived from multiple files (e.g. [IncludeFile])</b><br>\
CAUTION: Using this interface may overwrite that!!\
</td></tr>\n";
   else
      *vecptr++ = "";

   FaoCheck (sizeof(FaoVector), &FaoVector, vecptr, FI_LI);
   status = FaolToNet (rqptr, SourceFao, &FaoVector);
   if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);
}

/*****************************************************************************/
/*
For administration menu configuration update pages provide the <form..> element
and some leading, commented-out descriptive text for the configuration file.
*/
 
void AdminMetaConBeginUpdateForm (REQUEST_STRUCT *rqptr)

{
   static char  BeginFormFao [] =
"<form method=\"POST\" action=\"!AZ\">\n\
<input type=\"hidden\" name=\"hidden$lf\" value=\"\
# Configuration:  !AZ&#94;\
#                 !AZ&#94;\
# Last Modified:  !20&W&#94;\
#                 !AZ.\'!AZ\'@!AZ&#94;\
\">";

   int  status;
   unsigned long  *vecptr;
   unsigned long  FaoVector [16];

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD__OTHER))
      WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER,
                 "AdminMetaConBeginUpdateForm()");

   vecptr = FaoVector;

   *vecptr++ = rqptr->rqHeader.PathInfoPtr;
   *vecptr++ = ServerHostPort;
   *vecptr++ = SoftwareID;
   *vecptr++ = &rqptr->rqTime.BeginTime64;
   *vecptr++ = rqptr->RemoteUser;
   *vecptr++ = rqptr->rqAuth.RealmDescrPtr;
   *vecptr++ = rqptr->ClientPtr->Lookup.HostName;

   FaoCheck (sizeof(FaoVector), &FaoVector, vecptr, FI_LI);
   status = FaolToNet (rqptr, BeginFormFao, &FaoVector);
   if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);
}

/*****************************************************************************/
/*
For administration menu configuration update pages provide the update buttons
and the </form> tag (complements AdminMetaConEndUpdateForm()).
*/
 
void AdminMetaConEndUpdateForm (REQUEST_STRUCT *rqptr)

{
   static char  EndForm [] =
"<p><input type=\"hidden\" name=\"hidden$lf\" value=\"&#94;&#94;# End!!&#94;\">\n\
<p><input type=\"submit\" value=\" Commit Changes \">\n\
<input type=reset value=\" Reset \">\n\
</form>\n";

   int  status;

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD__OTHER))
      WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER,
                 "AdminMetaConEndUpdateForm()");

   status = FaolToNet (rqptr, EndForm, NULL);
   if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);
}

/*****************************************************************************/
/*
For administration menu configuration update pages provide the update buttons
and the </form> tag (complements AdminMetaConEndUpdateForm()).
*/
 
void AdminVirtualServiceForm
(
REQUEST_STRUCT *rqptr,
char *FormAction,
char *VirtualService,
BOOL UseServerConfig
)
{
   static char  BeginFormFao [] =
"<form action=\"!AZ\">\n\
<input type=\"hidden\" name=\"server\" value=\"!&?yes\rno\r\">\n\
<select name=\"virtual\">\n\
<option!&?\r SELECT\r value=\"\">!&?none configured\rfor all services\r\n";

   static char  EndFormFao [] =
"</select>\n\
<input type=\"submit\" value=\" Service \">\n\
</form>\n";

   int  status;
   unsigned long  *vecptr;
   unsigned long  FaoVector [16];
   SERVICE_STRUCT *svptr;

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD__OTHER))
      WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER,
                 "AdminVirtualServiceForm()");

   vecptr = FaoVector;
   *vecptr++ = FormAction;
   *vecptr++ = UseServerConfig;
   *vecptr++ = VirtualService[0];
   *vecptr++ = (ServiceList.EntryCount == 1);
   FaoCheck (sizeof(FaoVector), &FaoVector, vecptr, FI_LI);
   status = FaolToNet (rqptr, BeginFormFao, &FaoVector);
   if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);

   if (ServiceList.EntryCount > 1)
   {
      LIST_ITERATE (svptr, &ServiceList)
      {
         vecptr = FaoVector;
         *vecptr++ = svptr->ServerHostPort;
         *vecptr++ = strsame (svptr->ServerHostPort, VirtualService, -1);
         *vecptr++ = svptr->ServerHostPort;

         FaoCheck (sizeof(FaoVector), &FaoVector, vecptr, FI_LI);
         status = FaolToNet (rqptr,
                     "<option value=\"!AZ\"!&? SELECTED\r\r>!AZ\n",
                             &FaoVector);
         if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);
      }
   }

   status = FaolToNet (rqptr, EndFormFao, NULL);
   if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);
}

/*****************************************************************************/
/*
The admin report page using an XMLHttpRequest() generates a response that
provides an HTTP RTT (round trip time) between the client browser and server. 
This RTT represents the underlying overhead for a request-response between
client and server.  The request header is parsed and immediately responded to
using a standard response header.  Both HTTP/1.n and HTTP/2 perform the same
underlying processing which is a stripped-down code path.  These requests are
not access logged.  The client (browser) calculates the RTT from
client-internal timestamps.  The internal overhead (in time) can be observed
from the associated request WATCH report line "STATUS 204 (No Content) rx:60
tx:365 bytes 0.977ms 435,227 B/s".  Observation shows it to be negligible.
*/
 
char* AdminPing (REQUEST_STRUCT *rqptr)

{
   static char pingJS [] =
"var pingGO;\n\
function doPing() {\n\
   var url = \'/rtt?ping\';\n\
   xhr = new XMLHttpRequest();\n\
   xhr.open(\'GET\', url, true);\n\
\
   xhr.onload = function() {\n\
      var mSecs = Date.now() - pingGO;\n\
      if (mSecs < 100) \n\
         var ping = mSecs + \'.0ms\';\n\
      else\n\
         var ping = mSecs + \'ms\';\n\
      document.getElementById(\'pingRTT\').innerHTML = \
\'&nbsp;(\' + ping + \')\';\n\
   };\n\
\
   pingGO = Date.now();\n\
   xhr.send();\n\
}\n\
setTimeout(doPing,200);\n";

   /*********/
   /* begin */
   /*********/

   if (!rqptr) return (pingJS);

   if (WATCHING (rqptr, WATCH_CONNECT))
      WatchThis (WATCHITM(rqptr), WATCH_CONNECT, "RTT PING!!");

   rqptr->PersistentRequest = rqptr->PersistentResponse =
      rqptr->rqPathSet.Expired = rqptr->rqPathSet.NoLog = true;
   ResponseHeader (rqptr, 204, NULL, 0, NULL, NULL);
   RequestEnd (rqptr);
   return (NULL);
}

/*****************************************************************************/
/*
When client IP address DNS host name lookup is not enabled specifically lookup
the host name of the client accessing the Server Admin.
*/
 
char* AdminLookYouUp (IPADDRESS *ipaptr)

{
   static BOOL  InProgress;
   static IPADDRESS  IpAddress;
   static TCPIP_HOST_LOOKUP  HostLookup;

   /*********/
   /* begin */
   /*********/

   if (WATCH_MODULE(WATCH_MOD__OTHER))
      WatchThis (WATCHALL, WATCH_MOD__OTHER, "AdminLookYouUp()");

   if (!ipaptr)
   {
      /* AST call after lookup */
      InProgress = false;
      return (NULL);
   }

   if (InProgress) return (NULL);

   /* if this (most recent) IP address has already been resolved */
   if (IPADDRESS_IS_SAME (ipaptr, &IpAddress))
      if (HostLookup.HostName[0] != '?')
         return (HostLookup.HostName);

   IPADDRESS_COPY (&IpAddress, ipaptr);

   /* lookup the IP address from server cache */
   if (VMSok (TcpIpCacheAddressToName (&HostLookup, &IpAddress)))
      if (HostLookup.HostName[0] != '?')
         return (HostLookup.HostName);

   /* lookup the IP address using DNS */
   InProgress = true;
   TcpIpAddressToName (&HostLookup, &IpAddress, 5, &AdminLookYouUp, NULL);
   return (NULL);
}

/*****************************************************************************/
/*
Javascript to allow selection of a page's refresh interval.
*/
 
char* AdminRefresh (REQUEST_STRUCT *rqptr)

{
    static char  *JsRefresh =
"<div id=\"wasdRefreshDiv\" style=\"display:none;\">\n\
<p>Refresh:&nbsp;\
<select id=\"refreshInterval\">\n\
<option value=\"0\">none</option>\n\
<option value=\"1\">1</option>\n\
<option value=\"2\">2</option>\n\
<option value=\"5\">5</option>\n\
<option value=\"10\">10</option>\n\
<option value=\"30\">30</option>\n\
</select>&nbsp;Seconds\n\
\
<script nonce=\"!AZ\">\n\
if (typeof norefresh == \'undefined\') \
document.getElementById(\"wasdRefreshDiv\").style.display = \'block\';\n\
var selfoc = false;\n\
var refint = 0;\n\
var refloc = window.location.search.search(\'refresh=\');\n\
if (refloc >= 0) refint = parseInt(window.location.search.substr(refloc+8));\n\
var selobj = document.getElementById(\'refreshInterval\');\n\
selobj.addEventListener(\"focus\",refreshFocus,false);\n\
selobj.addEventListener(\"blur\",refreshBlur,false);\n\
selobj.addEventListener(\"change\",refreshChange,false);\n\
switch (refint)\n\
{\n\
   case 1 : selobj.selectedIndex = 1; break;\n\
   case 2 : selobj.selectedIndex = 2; break;\n\
   case 5 : selobj.selectedIndex = 3; break;\n\
   case 10 : selobj.selectedIndex = 4; break;\n\
   case 30 : selobj.selectedIndex = 5; break;\n\
   default : selobj.selectedIndex = 0;\n\
}\n\
if (refint) setTimeout(refreshNow,refint*1000);\n\
\
function refreshFocus()\n\
{\n\
   selfoc = true;\n\
}\n\
\
function refreshBlur()\n\
{\n\
   selfoc = false;\n\
}\n\
\
function refreshChange()\n\
{\n\
   selfoc = false;\n\
   refreshNow();\n\
}\n\
\
function refreshNow()\n\
{\n\
   if (selfoc) return;\n\
   var value = selobj.value;\n\
   var href = window.location.href;\n\
   var rloc = href.indexOf(\'refresh=\');\n\
   if (rloc > 0)\n\
      href = href.substr(0,rloc) + \'refresh=\' + value;\n\
   else\n\
   if (window.location.search.length > 1)\n\
      href += \'&refresh=\' + value;\n\
   else\n\
      href += \'?refresh=\' + value;\n\
   window.location.replace(href);\n\
}\n\
\
</script>\n\
</div>\n";

   char  *bptr;

   /*********/
   /* begin */
   /*********/

   if (WATCH_MODULE(WATCH_MOD__OTHER))
      WatchThis (WATCHALL, WATCH_MOD__OTHER, "AdminRefresh()");

   bptr = FaoToMemory (rqptr, JsRefresh, GenerateCspNonce(rqptr));

   return (bptr);
}

/*****************************************************************************/
/*
CSS for WASD administration pages.
*/
 
char* AdminWasdCss ()

{
    static char  *WasdCss =
"<style type=\"text/css\">\n\
body { background-color:#ffffff; color:#000000; \
font-family:Arial,Helvetica,Sans-serif; }\n\
div.wasd { margin:1.5em 1em 1.5em 1em; }\n\
\
table { margin:0.5em 0.5em 0.5em 0; border-collapse:collapse; }\n\
td,th { padding:0.2em 0.3em 0.1em 0.3em; vertical-align:text-top; \
white-space:nowrap; }\n\
\
table.ctgry { background-color:#f8f8f8; border:1px solid #696969; }\n\
table.ctgry .ctttl { padding:0.2em; border:1px solid #696969; \
font-size:105%; }\n\
\
table.rghtlft { border-style:none; }\n\
table.rghtlft th { text-align:right; }\n\
table.rghtlft td { text-align:left; }\n\
\
table.rghtrght { border-style:none; }\n\
table.rghtrght th, table.rghtrght td { text-align:right; }\n\
\
table.lftlft { border-style:none; }\n\
table.lftlft th, table.lftlft td { text-align:left; }\n\
\
input[type=submit], input[type=reset], .abttn \
{ display:inline-block; position:relative; \
min-width:7em; margin:0.1em 0.2em 0.1em 0.2em; \
padding:0.1em 0.25em 0.1em 0.25em; \
border: 1px solid #444; border-radius:0.2em; \
background-color:#fefefe; background:linear-gradient\
(to bottom,#fefefe,#ebebeb); \
color:#222222; font-size:80%; text-align:center; text-decoration:none; \
}\n\
\
input[type=checkbox], input[type=radio] { vertical-align:text-bottom; }\n\
\
.abttn { min-width:4em; }\n\
\
.alert { display:block; font-weight:bold; font-size:110%; color:red; }\n\
\
input[type=submit]:disabled, input[type=reset]:disabled \
{ font-style:italic; }\n\
\
.bttn090 { min-width:0.9em!important; }\n\
.bttn134 { min-width:1.34em!important; }\n\
.bttn200 { min-width:2em!important; }\n\
.bttn290 { min-width:2.9em!important; }\n\
.bttn300 { min-width:3em!important; }\n\
.bttn400 { min-width:4em!important; }\n\
.bttn420 { min-width:4.2em!important; }\n\
.bttn425 { min-width:4.25em!important; }\n\
.bttn500 { min-width:5em!important; }\n\
.bttn600 { min-width:6em!important; }\n\
.bttn700 { min-width:7em!important; }\n\
.bttn800 { min-width:8em!important; }\n\
.dbttn { color:#585858; border:1px dotted black; }\n\
\
.alnk { }\n\
.hlght { background-color:#ededed; }\n\
.lftop th,td { padding:0.1em 0.2em 0 0.3em; text-align:left; }\n\
.maxvis { max-width:10em; overflow-x:visible; }\n\
.outa { text-decoration:none; color:inherit; }\n\
.outu { line-height:1.0em; margin-left:2px; }\n\
.pdlft1 { padding-left:1em; }\n\
.pdrght1 { padding-right:1em; }\n\
.px1 { margin:1px; }\n\
.sbttl { padding-bottom:0.3em!important; font-weight:bold; \
text-decoration:underline; }\n\
.struck { text-decoration:line-through; }\n\
.tacnt { text-align:center!important; }\n\
.talft { text-align:left!important; }\n\
.targht { text-align:right!important; }\n\
.txt { font-family:monospace; font-size:115%; }\n\
.pretxt { font-family:monospace; font-size:115%; white-space:pre; }\n\
.smacap { font-variant: small-caps; }\n\
.include { font-size:110%; background-color:#ebebeb; margin:0.2em; }\n\
.incomment { font-style:italic; }\n\
.incounter { background-color:#eaeaea; padding:0.2em; }\n\
\
</style>\n";

   /*********/
   /* begin */
   /*********/

   if (WATCH_MODULE(WATCH_MOD__OTHER))
      WatchThis (WATCHALL, WATCH_MOD__OTHER, "AdminWasdCss()");

   return (WasdCss);
}

/****************************************************************************/

