/*****************************************************************************/
/*
                                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
---------------
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 PercentOf() 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 <stdio.h>
#include <ctype.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"

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

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

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

extern int  ToLowerCase[],
            ToUpperCase[];

extern unsigned long  HttpdStartTime64[];

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

extern char  *AuthConfigHtaDirectory,
             *GzipZlibNamePtr,
             *GzipZlibVersionPtr;

#define acptr AccountingPtr
extern ACCOUNTING_STRUCT  *AccountingPtr;
extern ACTIVITY_GBLSEC  *ActivityGblSecPtr;
extern CONFIG_STRUCT  Config;
extern HTTPD_GBLSEC  *HttpdGblSecPtr;
extern HTTPD_PROCESS  HttpdProcess;
extern IPADDRESS  TcpIpEmptyAddress;
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,
         UseServerDatabase;
   int  status;
   unsigned short  Length;
   char  *cptr;
   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));

   /* 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 (rqptr->rqHeader.RequestUriPtr, HTTPD_ADMIN, -1))
      rqptr->rqResponse.NoGzip = true;
   else
   if (strsame (rqptr->rqHeader.RequestUriPtr, ADMIN_REPORT_WATCH, -1))
      rqptr->rqResponse.NoGzip = true;

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

#ifdef NOT_FULLY_IMPLEMENTED_YET

   if (strsame (rqptr->rqHeader.PathInfoPtr, HTTPD_VS_ADMIN, -1))
   {
      AdminVirtualServerMenu (rqptr);
      return;
   }

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

   if (strsame (rqptr->rqHeader.PathInfoPtr, 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 ((rqptr->rqHeader.PathInfoPtr+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 (rqptr->rqHeader.PathInfoPtr, 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 (rqptr->rqHeader.PathInfoPtr, ADMIN_REVISE_HTA,
                sizeof(ADMIN_REVISE_HTA)-1))
   {
      if (VMSnok (status = AdminParsePath (rqptr, NULL)))
         AdminEnd (rqptr);
      else
         HTAdminBegin (rqptr);
      return;
   }

   if (strsame (rqptr->rqHeader.PathInfoPtr, 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 ((rqptr->rqHeader.PathInfoPtr+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 (rqptr->rqHeader.PathInfoPtr, 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 (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_ACTIVITY, -1))
   {
      GraphActivityReport (rqptr);
      return;
   }

   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_MATCH, -1))
   {
      StringMatchReport (rqptr);
      return;
   }

   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_PROCESS, -1))
   {
      WatchProcessReport (rqptr);
      return;
   }

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

   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_WATCH_STRUCT, -1))
   {
      WatchReportStruct (rqptr);
      return;
   }

   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_WEBDAV, -1))
   {
      DavWebReport (rqptr);
      return;
   }

   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_WEBDAV_XML, -1))
   {
      DavXmlReport (rqptr);
      return;
   }

   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_WEBSOCKET, -1))
   {
      WebSockReport (rqptr);
      return;
   }

   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_CONTROL_DELETE_PROCESS, -1))
   {
      /********************/
      /* delete a process */
      /********************/

      WatchDeleteProcess (rqptr);
      return;
   }

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

   ClusterDo = SlashDo = false;
   UseServerDatabase = 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 (TOUP(FieldValue[0]) == 'Y')
            UseServerDatabase = false;
         else
         if (TOUP(FieldValue[0]) == 'N')
            UseServerDatabase = 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 (TOUP(FieldValue[0]) == 'Y')
            UseServerDatabase = true;
         else
         if (TOUP(FieldValue[0]) == 'N')
            UseServerDatabase = 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 (strsame (DoThis, "HELP", -1))
         ControlDoHelp (rqptr);
      else
      if (strsame (DoThis, "STATUS", -1))
         InstanceStatusCliReport (rqptr);
      else
         AdminControl (rqptr, DoThis, ClusterDo);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, HTTPD_ADMIN, -1) ||
       strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT, -1) ||
       strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REVISE, -1) ||
       strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_CONTROL, -1))
   {
      /********/
      /* menu */
      /********/

      AdminMenu (rqptr);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_ALIGN, -1))
   {
      HttpdAlignReport (rqptr);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_AUTH_USER, -1))
   {
      AuthCacheReport (rqptr);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_CACHE, -1))
   {
      CacheReport (rqptr, false);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_CACHE_ENTRIES, -1))
   {
      if (Md5HashHexString[0])
         CacheReportEntry (rqptr, Md5HashHexString);
      else
         CacheReport (rqptr, true);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_CLUSTER, -1))
   {
      WatchShowCluster (rqptr);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_CONFIG, -1) ||
       strsame (rqptr->rqHeader.PathInfoPtr, 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 (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_CONFIG, -1))
            ConfigReport (rqptr, UseServerDatabase);
         else
            ConfigRevise (rqptr, UseServerDatabase);
      }
      else
         FileBegin (rqptr, &AdminEnd, NULL, NULL,
                    rqptr->ParseOds.ExpFileName, "text/plain");
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_DCL, -1))
   {
      DclScriptingReport (rqptr);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_HOSTCACHE, -1))
   {
      TcpIpHostCacheReport (rqptr);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_PROCESS, -1))
   {
      WatchShowProcess (rqptr, ProcessIdString, ProcessIdUserName);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_DECNET, -1))
   {
      DECnetScriptingReport (rqptr);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_HTTP, -1))
   {
      Http2Report (rqptr);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_HPACK, -1))
   {
      HpackReport (rqptr, Http2Connection);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_LOCK, -1))
   {
      InstanceLockReport (rqptr);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_PROCESS_INPUT, -1))
   {
      AdminServerProcessInput (rqptr);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_PROCESS_OUTPUT, -1))
   {
      AdminServerProcessOutput (rqptr);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_MEMORY, -1))
   {
      VmReport (rqptr);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_MESSAGES, -1) ||
       strsame (rqptr->rqHeader.PathInfoPtr, 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 (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_MESSAGES, -1))
            MsgConfigReport (rqptr, UseServerDatabase);
         else
            MsgConfigRevise (rqptr, UseServerDatabase);
      }
      else
         FileBegin (rqptr, &AdminEnd, NULL, NULL,
                    rqptr->ParseOds.ExpFileName, "text/plain");
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_AUTH_PATHS, -1) ||
       strsame (rqptr->rqHeader.PathInfoPtr, 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, UseServerDatabase, VirtualHostPort);
      else
         FileBegin (rqptr, &AdminEnd, NULL, NULL,
                    rqptr->ParseOds.ExpFileName, "text/plain");
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_PROXY, -1))
   {
      ProxyMaintReport (rqptr);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_PROXY_PERSISTENT, -1))
   {
      ProxyNetPersistentReport (rqptr);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr,
                ADMIN_REPORT_REQUEST, sizeof(ADMIN_REPORT_REQUEST)-1))
   {
      RequestReport (rqptr);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_SERVICES, -1) ||
       strsame (rqptr->rqHeader.PathInfoPtr, 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 (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_SERVICES, -1))
            ServiceReport (rqptr, UseServerDatabase);
         else
            ServiceConfigRevise (rqptr, UseServerDatabase);
      }
      else
         FileBegin (rqptr, &AdminEnd, NULL, NULL,
                    rqptr->ParseOds.ExpFileName, "text/plain");
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_SERVICE_LOG, -1))
   {
      LoggingReport (rqptr, VirtualHostPort);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_SHOW_PROCESS, -1))
   {
      WatchShowProcess (rqptr, ProcessIdString, ProcessIdUserName);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_STATS, -1))
   {
      AdminReportServerStats (rqptr);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_MAPPING, -1) ||
       strsame (rqptr->rqHeader.PathInfoPtr, 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, UseServerDatabase, VirtualHostPort);
      else
         FileBegin (rqptr, &AdminEnd, NULL, NULL,
                    rqptr->ParseOds.ExpFileName, "text/plain");
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_SSL, -1) ||
       strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_SSL_CLIENT, -1))
   {
      SesolaReport (rqptr, VirtualHostPort);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_SSL_CA, -1) ||
       strsame (rqptr->rqHeader.PathInfoPtr, 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;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_SUPERVISOR, -1))
   {
      HttpdSupervisorReport (rqptr);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_SYSTEM, -1))
   {
      WatchShowSystem (rqptr);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_SYSTEM_PLUS, -1))
   {
      sysPlusReport (rqptr);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_THROTTLE, -1))
   {
      ThrottleReport (rqptr);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, 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;

   RequestDclAst (rqptr, AdminEnd, 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_CONFIG, -1) ||
       strsame (cptr, ADMIN_REVISE_CONFIG, -1))
      sptr = v10orPrev10(CONFIG_FILE_NAME,-1);
   else
   if (strsame (cptr, ADMIN_REPORT_MESSAGES, -1) ||
       strsame (cptr, ADMIN_REVISE_MESSAGES, -1))
      sptr = v10orPrev10(CONFIG_MSG_FILE_NAME,-1);
   else
   if (strsame (cptr, ADMIN_REPORT_AUTH_PATHS, -1) ||
       strsame (cptr, ADMIN_REVISE_AUTH_PATHS, -1))
      sptr = v10orPrev10(CONFIG_AUTH_FILE_NAME,-1);
   else
   if (strsame (cptr, ADMIN_REPORT_MAPPING, -1) ||
       strsame (cptr, ADMIN_REVISE_MAPPING, -1))
      sptr = v10orPrev10(CONFIG_MAP_FILE_NAME,-1);
   else
   if (strsame (cptr, ADMIN_REPORT_SERVICES, -1) ||
       strsame (cptr, ADMIN_REVISE_SERVICES, -1))
      sptr = v10orPrev10(CONFIG_SERVICE_FILE_NAME,-1);
   else
   if (strsame (cptr, ADMIN_REVISE_SITELOG, -1) ||
       strsame (cptr, ADMIN_REVISE_SITELOG_ENTRY, -1))
   {
      if (!(sptr = v10orPrev10(CONFIG_SITELOG_FILE_NAME,0)))
         sptr = v10orPrev10(DEFAULT_SITELOG_FILE_NAME,0);
   }
   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 = v10orPrev10(CONFIG_SSL_CAFILE,-1);
      }
      else
         sptr = v10orPrev10(CONFIG_SSL_CAFILE,-1);
   }
   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;

   if (strsame (cptr, ADMIN_CONTROL_PROXY_ADJUST, -1))
   {
      /* proxy serving adjustment menu */
      ProxyMaintAdjust (rqptr);
      return;
   }

   if (strsame (cptr, ADMIN_CONTROL_PROXY_ADJUST_NOW, -1))
   {
      /* adjust proxy serving configuration */
      ProxyMaintControl (rqptr);
      return;
   }

   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_DECNET_DISCONNECT, -1))
      sptr = CONTROL_DECNET_DISCONNECT;
   else
   if (strsame (cptr, ADMIN_CONTROL_DECNET_PURGE, -1))
      sptr = CONTROL_DECNET_PURGE;
   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_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_PROXY_ON, -1))
      sptr = CONTROL_PROXY_ON;
   else
   if (strsame (cptr, ADMIN_CONTROL_PROXY_OFF, -1))
      sptr = CONTROL_PROXY_OFF;
   else
   if (strsame (cptr, ADMIN_CONTROL_PROXY_PURGE_BCKGRND, -1))
      sptr = CONTROL_PROXY_PURGE_BCKGRND;
   else
   if (strsame (cptr, ADMIN_CONTROL_PROXY_PURGE_REACTIVE, -1))
      sptr = CONTROL_PROXY_PURGE_REACTIVE;
   else
   if (strsame (cptr, ADMIN_CONTROL_PROXY_PURGE_ROUTINE, -1))
      sptr = CONTROL_PROXY_PURGE_ROUTINE;
   else
   if (strsame (cptr, ADMIN_CONTROL_PROXY_PURGE_HOST, -1))
      sptr = CONTROL_PROXY_PURGE_HOST;
   else
   if (strsame (cptr, ADMIN_CONTROL_PROXY_STATISTICS, -1))
      sptr = CONTROL_PROXY_STATISTICS;
   else
   if (strsame (cptr, ADMIN_CONTROL_PROXY_STOP_SCAN, -1))
      sptr = CONTROL_PROXY_STOP_SCAN;
   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_ZERO_NOTICED, -1))
      sptr = CONTROL_ZERO_NOTICED;
   else
   if (strsame (cptr, ADMIN_CONTROL_ZERO_PROXY, -1))
      sptr = CONTROL_ZERO_PROXY;
   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);

   strcat (cptr, ".");
   if (*cptr == '!')
   {
      rqptr->rqResponse.HttpStatus = 403;
      cptr[1] = TOUP(cptr[1]);
      ErrorGeneral (rqptr, cptr+1, FI_LI);
   }
   else
   {
      cptr[0] = TOUP(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 [] = { 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 language=\"JavaScript\">\n\
<!!--\n\
function confact (btn,msg1,msg2) {\n\
   this.btn = btn;\n\
   this.msg1 = msg1;\n\
   this.msg2 = msg2;\n\
}\n\
var confary = new Array();\n\
confary[0] = new confact \
(\"restart\",\"Restart \",\" after allowing current requests to complete?\");\n\
confary[1] = new confact \
(\"restart/now\",\"Restart \",\" DISCONNECTING current requests?\");\n\
confary[2] = new confact \
(\"restart/quiet\",\"Restart \",\" when current requests reach zero?\");\n\
confary[3] = new confact \
(\"exit\",\"Exit \",\" allowing current requests to complete?\");\n\
confary[4] = new confact \
(\"exit/now\",\"Exit \",\" DISCONNECTING current requests?\");\n\
confary[6] = new confact \
(\"zero\",\"Zero \",\" server accounting?\");\n\
confary[7] = new confact \
(\"zero/proxy\",\"Zero \",\" proxy accounting?\");\n\
confary[8] = new confact \
(\"active\",\"Allow \",\" network connections to all instances \
(active mode)?\");\n\
confary[9] = new confact \
(\"passive\",\"Limit \",\" network connections to just one instance \
(passive mode)?\");\n\
confary[10] = new confact \
(\"net/purge\",\"Purge \",\" persistent (idle) network connections?\");\n\
confary[11] = new confact \
(\"net/purge/all\",\"Purge \",\" all (idle and in-progress) \
network connections, DISCONNECTING current requests?\");\n\
\
function doIt(link,btn) {\n\
!&@\
   for (idx in confary)\n\
      if (confary[idx].btn == btn) \
return confirm (confary[idx].msg1 + scope + confary[idx].msg2);\n\
   return true;\n\
}\n\
// 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\
// -->\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 class=\"abttn bttn300\" \
href=\"!AZ\" onClick=\"return doIt(this,\'zero\')\">Zero</a></td>\n\
</tr>\n\
\
<tr>\n\
<th>Configuration</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 class=\"abttn bttn300\" href=\"!AZ\" \
onClick=\"return doIt(this,\'net/purge\')\">Purge</a></td>\n\
<td><a class=\"abttn bttn200\" href=\"!AZ\" \
onClick=\"return doIt(this,\'net/purge/all\')\">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 class=\"abttn bttn400\" href=\"!AZ\" \
onClick=\"return doIt(this,null)\">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 class=\"abttn bttn400\" href=\"!AZ\" \
onClick=\"return doIt(this,null)\">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 class=\"abttn bttn400\" href=\"!AZ\" \
onClick=\"return doIt(this,null)\">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>!&,@SQ</td></tr>\n\
\
!&@\
\
</table>\n\
\
</td></tr>\n\
<tr><td>\n\
\
<table class=\"admn1\">\n\
<tr><td>\n\
\
<table class=\"admn1\">\n\
<tr><td>\
<a class=\"abttn bttn800\" href=\"!AZ\" \
onClick=\"return doIt(this,\'restart\')\">RESTART</a>\
</td></tr>\n\
<tr><td>\
<a class=\"abttn bttn800\" href=\"!AZ\" \
onClick=\"return doIt(this,\'restart/now\')\">\
<span style=\"font-size:85%;\">RESTART</span>NOW\
</a>\
</td></tr>\n\
<tr><td>\
<a class=\"abttn bttn800\" href=\"!AZ\" \
onClick=\"return doIt(this,\'restart/quiet\')\">\
<span style=\"font-size:85%;\">RESTART</span>Quiet\
</a>\
</td></tr>\n\
<tr><td>\
<a class=\"abttn bttn800\" href=\"!AZ\" \
onClick=\"return doIt(this,\'exit\')\">EXIT</a>\
</td></tr>\n\
<tr><td>\
<a class=\"abttn bttn800\" href=\"!AZ\" \
onClick=\"return doIt(this,\'exit/now\')\">\
<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>Proxy</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>var norefresh = true;</script>\n\
!AZ\
\
</div>\n\
</body>\n\
</html>\n";

   static unsigned long  LibDeltaHours = LIB$K_DELTA_HOURS;

   static unsigned short  SyiClusterNodes;
   static unsigned long  JpiCpuTime,
                         Pid = 0;
   static unsigned long  ActivityUpTime [QUAD2],
                         CurrentTime64 [QUAD2],
                         ImageUpTime [QUAD2],
                         JpiLoginTime [QUAD2],
                         ProcessUpTime [QUAD2],
                         SystemUpTime [QUAD2];
   static VMS_ITEM_LIST3  JpiItems [] =
   {
      { sizeof(JpiCpuTime), JPI$_CPUTIM, &JpiCpuTime, 0 },
      { sizeof(JpiLoginTime), JPI$_LOGINTIM, &JpiLoginTime, 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;
   unsigned short  Length;
   unsigned long  ActivityDeltaHours;
   unsigned long  *vecptr;
   unsigned long  BytesTotal [QUAD2],
                  FaoVector [192];
   char  *cptr,
         *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->Http2Stream.Http2Ptr, 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 (&CurrentTime64);
   lib$sub_times (&CurrentTime64, &SysInfo.BootTime64, &SystemUpTime);
   lib$sub_times (&CurrentTime64, &HttpdStartTime64, &ImageUpTime);
   lib$sub_times (&CurrentTime64, &JpiLoginTime, &ProcessUpTime);

   if (ActivityTotalMinutes)
   {
      InstanceMutexLock (INSTANCE_MUTEX_HTTPD);
      lib$sub_times (&CurrentTime64, &ActivityGblSecPtr->StartTime64,
                     &ActivityUpTime);
      InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);
      lib$cvt_from_internal_time (&LibDeltaHours, &ActivityDeltaHours,
                                  &ActivityUpTime);
   }
   else
      ActivityDeltaHours = 0;

   /* 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;

   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++ = 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++ = "<a class=\"abttn bttn300\" href=\"!AZ\">Log</a>";
      *vecptr++ = ADMIN_REPORT_PROCESS_OUTPUT;
   }
   else
      *vecptr++ = "<a class=\"abttn bttn300 dbttn\">Log</a>";

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

   /* configuration */
   *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;

   /* 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 class=\"abttn bttn400\" href=\"!AZ\" \
onClick=\"return doIt(this,null)\">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 */
   if (HttpdAlignFaultReport)
   {
      *vecptr++ = "<a class=\"abttn bttn400\" href=\"!AZ\">AlnFlt</a>";
      *vecptr++ = ADMIN_REPORT_ALIGN;
   }
   else
      *vecptr++ = "<a class=\"abttn bttn400 dbttn\">AlnFlt</a>";

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

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

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

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

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

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

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

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

   *vecptr++ = "<a class=\"abttn bttn400\" href=\"!AZ\">Memory</a>";
   *vecptr++ = ADMIN_REPORT_MEMORY;

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

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

   *vecptr++ = "<a class=\"abttn bttn400\" href=\"!AZ\">Request</a>";
   *vecptr++ = ADMIN_REPORT_REQUEST;

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

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

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

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

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

   for (idx = 0; ActHours[idx]; idx++)
   {
      if (ActivityTotalMinutes && ActivityDeltaHours >= ActHours[idx]-1)
      {
         *vecptr++ = "<a class=\"abttn bttn125\" href=\"!AZ?of=!UL\">!-!UL</a>";
         *vecptr++ = ADMIN_REPORT_ACTIVITY;
         *vecptr++ = ActHours[idx];
      }
      else
      {
         *vecptr++ = "<a class=\"abttn bttn125 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))
   {
      if (rqptr->Http2Stream.Http2Ptr->PingMicroSeconds)
      {
         *vecptr++ = "HTTP/2&nbsp;!UL.!ULmS";
         *vecptr++ = rqptr->Http2Stream.Http2Ptr->PingMicroSeconds / 1000;
         *vecptr++ = rqptr->Http2Stream.Http2Ptr->PingMicroSeconds % 1000;
      }
      else
         *vecptr++ = "HTTP/2";
   }
   else
   if (rqptr->rqResponse.HttpVersion == HTTP_VERSION_1_1)
      *vecptr++ = "HTTP/1.1";
   else
      *vecptr++ = "HTTP/1.0";

   /* informational accounting */
   *vecptr++ = SysInfo.NodeName;
   *vecptr++ = &SystemUpTime;
   if (NodeInstanceCount > 1)
      *vecptr++ = HttpdProcess.PrcNam;
   else
      *vecptr++ = "Process";
   *vecptr++ = &ProcessUpTime;
   *vecptr++ = &ImageUpTime;
   *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];
   PLUS_QUAD_QUAD (acptr->BytesRawRx[HTTP12],
                   acptr->BytesRawTx[HTTP12],
                   BytesTotal);
   *vecptr++ = &BytesTotal;

   if (LoggingFileError)
      *vecptr++ =
"<font color=\"#ff0000\"><b>Problem writing access logs!!</font>";
   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 class=\"abttn bttn200\" href=\"!AZ\" \
onClick=\"return doIt(this,null)\">Off</a>\
<a class=\"abttn bttn300\" href=\"!AZ\" \
onClick=\"doIt(this,null)\">Flush</a>";
      *vecptr++ = ADMIN_CONTROL_LOG_CLOSE;
      *vecptr++ = ADMIN_CONTROL_LOG_FLUSH;
   }
   else
   {
      *vecptr++ =
"<a class=\"abttn bttn200\" href=\"!AZ\" \
onClick=\"return doIt(this,null)\">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 class=\"abttn bttn200\" href=\"!AZ\" \
onClick=\"return doIt(this,null)\">Off</a>\
<a class=\"abttn bttn300\" href=\"!AZ\" \
onClick=\"doIt(this,null)\">Purge</a>";
      *vecptr++ = ADMIN_CONTROL_CACHE_OFF;
      *vecptr++ = ADMIN_CONTROL_CACHE_PURGE;
   }
   else
   {
      *vecptr++ =
"<a class=\"abttn bttn200\" href=\"!AZ\" \
onClick=\"return doIt(this,null)\">On</a>\
<a class=\"abttn bttn200 dbttn\">Off</a>\
<a class=\"abttn bttn300 dbttn\">Flush</a>";
      *vecptr++ = ADMIN_CONTROL_CACHE_ON;
   }

   /* proxy control  */
   if (ProxyServiceCount())
   {
      *vecptr++ =
"<a class=\"abttn bttn400\" href=\"!AZ\" \
onClick=\"return doIt(this,\'proxy/adjust\')\">Adjust</a>\
<a class=\"abttn bttn400\" href=\"!AZ\" \
onClick=\"return doIt(this,\'zero/proxy\')\">Zero</a>";
      *vecptr++ = ADMIN_CONTROL_PROXY_ADJUST;
      *vecptr++ = ADMIN_CONTROL_ZERO_PROXY;
   }
   else
      *vecptr++ =
"<a class=\"abttn bttn400 dbttn\">Adjust</a>\
<a class=\"abttn bttn400 dbttn\">Zero</a>";

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

   if (!InstanceStartupMax)
   {
      *vecptr++ =
"<a class=\"abttn bttn290\" href=\"!AZ/max\" \
onClick=\"return doIt(this,null)\">Max</a>";
      *vecptr++ = ADMIN_CONTROL_INSTANCE;
   }
   else
      *vecptr++ = "<a class=\"abttn bttn290 dbttn\">Max</a>";

   if (InstanceStartupMax != INSTANCE_PER_CPU)
   {
      *vecptr++ =
"<a class=\"abttn bttn290\" href=\"!AZ/cpu\" \
onClick=\"return doIt(this,null)\">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++ = ADMIN_CONTROL_INSTANCE;
         *vecptr++ = cnt;
         status = FaolToNet (rqptr,
"<a class=\"abttn bttn090\" href=\"!AZ/!UL\" \
onClick=\"return doIt(this,null)\">!-!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 class=\"abttn bttn400\" href=\"!AZ\" \
onClick=\"return doIt(this,\'active\')\">Active</a>\
<a class=\"abttn bttn400 dbttn\">Passive</a>";
      *vecptr++ = ADMIN_CONTROL_INSTANCE_ACTIVE;
   }
   else
   {
      *vecptr++ =
"<a class=\"abttn bttn400 dbttn\">Active</a>\
<a class=\"abttn bttn400\" href=\"!AZ\" \
onClick=\"return doIt(this,\'passive\')\">Passive</a>";
      *vecptr++ = ADMIN_CONTROL_INSTANCE_PASSIVE;
   }

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

   InstanceStatusAdminReport (rqptr);

   status = FaoToNet (rqptr, EndPageFao, AdminRefresh());
   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 unsigned long  CurrentTime64 [QUAD2],
                         JpiLoginTime [QUAD2],
                         SystemUpTime [QUAD2];
   static VMS_ITEM_LIST3  JpiItems [] =
   {
      { sizeof(JpiLoginTime), JPI$_LOGINTIM, &JpiLoginTime, 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 (&CurrentTime64);
   lib$sub_times (&CurrentTime64, &SysInfo.BootTime64, &SystemUpTime);

   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++ = &SystemUpTime;

   *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_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 */))


/*
   VAX VMS does not seem to like "!@UQ" and must be made "!@UJ"!!!
   The VAX version not supporting this doesn't seem to be documented,
   but at run-time returns a %X00000014 status (%SYSTEM-F-BADPARAM).
   Implication: VAX version can only report a maximum of 2^32-1 bytes, or
   4,294,967,295 before overflow, AXP 2^64-1 (calculator gave an ERROR :^)
*/

   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 !ULMB 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></td></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 style=\"max-width:13em;font-size:90%;\">\
!UL&nbsp;/&nbsp;!UL&nbsp;(!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\">!&,@SQ</td></tr>\n\
<tr><th>/Tx:</th><td colspan=\"2\">!&,@SQ</td></tr>\n\
<tr><th>Error&nbsp;&nbsp;/Rx:</th><td>!&L</td></tr>\n\
<tr><th>/Tx:</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>!&,@SQ</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>!&,@SQ</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>Connect&nbsp;&nbsp;/All:</th><td>!&L</td></tr>\n\
<tr><th>/Reused:</th><td>!&L</td></tr>\n\
<tr><th>CGI:</th><td>!&L</td></tr>\n\
<tr><th>OSU:</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>!&,@SQ</td>\
<td>!&,@SQ</td>\
<td>!UL%</td>\
</tr>\n\
<tr><th>Inflate:</th>\
<td>!&L</td>\
<td>!UL%</td>\
<td>!&,@SQ</td>\
<td>!&,@SQ</td>\
<td>!UL%</td>\
</tr>\n\
<tr><th>Total&nbsp;Tx:</th>\
<td>!&L</td>\
<td>100%</td>\
<td>!&,@SQ</td>\
<td>!&,@SQ</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\
\
<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\
!AZ\
<tr><th>Alignment Fault:</th><td>!&L</td></tr>\n\
<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 [13],
                LastExitStatus [16],
                LastExitTime [21];

   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  CurrentTime64 [QUAD2],
                         ImageUpTime [QUAD2],
                         JpiAuthPriv [QUAD2],
                         JpiCurPriv [QUAD2],
                         JpiImagPriv [QUAD2],
                         JpiLoginTime [QUAD2],
                         ProcessUpTime [QUAD2];

   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(JpiLoginTime), JPI$_LOGINTIM, &JpiLoginTime, 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 }
   };

   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],
                  ResponseDuration [QUAD2];
   char  *cptr, *sptr,
         *AuthPrivPtr,
         *CurPrivPtr,
         *ImagPrivPtr,
         *ClusterInstancePtr,
         *NodeInstancePtr,
         *SesolaVersionPtr;
   char  EnvNumber [16];
   IO_SB  IOsb;
   ODS_STRUCT  WasdRootOds;

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

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

   SesolaVersionPtr = SesolaVersion (false);

   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 (&CurrentTime64);
   lib$sub_times (&CurrentTime64, &HttpdStartTime64, &ImageUpTime);
   lib$sub_times (&CurrentTime64, &JpiLoginTime, &ProcessUpTime);

   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
      strcpy (LastExitStatus, "<i>(none)</i>");

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

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

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

   vecptr = FaoVector;

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

   *vecptr++ = SoftwareID;
   *vecptr++ = BuildInfo;
   if (SesolaVersionPtr[0])
      *vecptr++ = SesolaVersionPtr;
   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.MemoryMB;
   *vecptr++ = SysInfo.Version;
   *vecptr++ = CommandLine;

   cptr = v10orPrev10(CONFIG_WASD_ROOT,-1);
   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++ = &ImageUpTime;
   *vecptr++ = &ProcessUpTime;
   *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 - PercentOf(JpiAstCnt,HttpdProcess.AstLm);
   *vecptr++ = JpiBioCnt;
   *vecptr++ = HttpdProcess.BioLm;
   *vecptr++ = 100 - PercentOf(JpiBioCnt,HttpdProcess.BioLm);
   *vecptr++ = JpiBytCnt;
   *vecptr++ = HttpdProcess.BytLm;
   *vecptr++ = 100 - PercentOf(JpiBytCnt,HttpdProcess.BytLm);
   *vecptr++ = JpiDioCnt;
   *vecptr++ = HttpdProcess.DioLm;
   *vecptr++ = 100 - PercentOf(JpiDioCnt,HttpdProcess.DioLm);
   *vecptr++ = JpiEnqCnt;
   *vecptr++ = HttpdProcess.EnqLm;
   *vecptr++ = 100 - PercentOf(JpiEnqCnt,HttpdProcess.EnqLm);
   *vecptr++ = JpiFilCnt;
   *vecptr++ = HttpdProcess.FilLm;
   *vecptr++ = 100 - PercentOf(JpiFilCnt,HttpdProcess.FilLm);
   *vecptr++ = JpiPagFilCnt;
   *vecptr++ = HttpdProcess.PgFlQuo;
   *vecptr++ = JpiPagFilCnt * 512 / 1048576;
   *vecptr++ = HttpdProcess.PgFlQuo * 512 / 1048576;
   *vecptr++ = 100 - PercentOf(JpiPagFilCnt,HttpdProcess.PgFlQuo);
   if (DclScriptDetachProcess)
      *vecptr++ = "(detached)";
   else
   {
      *vecptr++ = "!&L&nbsp;/&nbsp;!&L&nbsp;(!UL%)";
      *vecptr++ = HttpdProcess.PrcLm - JpiPrcCnt;
      *vecptr++ = HttpdProcess.PrcLm;
      *vecptr++ = 100 - PercentOf(HttpdProcess.PrcLm-JpiPrcCnt,
                                  HttpdProcess.PrcLm);
   }
   *vecptr++ = JpiTqCnt;
   *vecptr++ = HttpdProcess.TqLm;
   *vecptr++ = 100 - PercentOf(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;
   }
   *vecptr++ = HttpdProcess.SysOutput;

   InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);

   *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++ = PercentOf(acptr->ConnectCount[HTTP2],
                         acptr->ConnectCount[HTTP12]);
   *vecptr++ = acptr->CurrentConnected[HTTP2];
   *vecptr++ = PercentOf(acptr->CurrentConnected[HTTP2],
                         acptr->CurrentConnected[HTTP12]);
   *vecptr++ = acptr->ConnectPeak[HTTP2];
   *vecptr++ = acptr->CurrentHttp1Persistent;
   *vecptr++ = acptr->ConnectPeakPersistent;
   *vecptr++ = acptr->ConnectIpv4Count;
   *vecptr++ = PercentOf(acptr->ConnectIpv4Count,
                         acptr->ConnectCount[HTTP12]);
   *vecptr++ = acptr->ConnectIpv6Count;
   *vecptr++ = PercentOf(acptr->ConnectIpv6Count,
                         acptr->ConnectCount[HTTP12]);
   *vecptr++ = acptr->ConnectSSLCount;
   *vecptr++ = PercentOf(acptr->ConnectSSLCount,
                         acptr->ConnectCount[HTTP12]);

   *vecptr++ = &acptr->BytesRawRx;
   *vecptr++ = &acptr->BytesRawTx;
   *vecptr++ = acptr->NetReadErrorCount;
   *vecptr++ = acptr->NetWriteErrorCount;

   /* requests */
   *vecptr++ = acptr->ProcessingTotalCount[HTTP12];
   *vecptr++ = acptr->CurrentProcessing[HTTP12];
   *vecptr++ = acptr->ProcessingPeak[HTTP12];
   *vecptr++ = acptr->ProcessingTooBusyCount;
   *vecptr++ = acptr->ProcessingTotalCount[HTTP2];
   *vecptr++ = PercentOf(acptr->ProcessingTotalCount[HTTP2],
                         acptr->ProcessingTotalCount[HTTP12]);
   *vecptr++ = acptr->CurrentProcessing[HTTP2];
   *vecptr++ = PercentOf(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++ = PercentOf(StringTotal,total);
   *vecptr++ = acptr->WebSocketRawCount;
   *vecptr++ = PercentOf(acptr->WebSocketCount,acptr->WebSocketRawCount);

   *vecptr++ = acptr->RequestSSLCount;
   *vecptr++ = PercentOf(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;

#ifndef __VAX
   /* 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
#endif /* __VAX */
      *vecptr++ = 0;
   *vecptr++ = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";

   /* HTTP versions */
   RequestHttpCount = acptr->RequestHttp2Count +
                      acptr->RequestHttp11Count +
                      acptr->RequestHttp10Count +
                      acptr->RequestHttp09Count;
   *vecptr++ = acptr->RequestHttp2Count;
   *vecptr++ = PercentOf (acptr->RequestHttp2Count, RequestHttpCount);
   *vecptr++ = acptr->RequestHttp11Count;
   *vecptr++ = PercentOf (acptr->RequestHttp11Count, RequestHttpCount);
   *vecptr++ = acptr->RequestHttp10Count;
   *vecptr++ = PercentOf (acptr->RequestHttp10Count, RequestHttpCount);
   *vecptr++ = acptr->RequestHttp09Count;
   *vecptr++ = PercentOf (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 */
   *vecptr++ = DurationString (rqptr, &acptr->ResponseDurationMin[HTTP12]);
   *vecptr++ = DurationString (rqptr, &acptr->ResponseDurationMax[HTTP12]);
   *vecptr++ = AverageDurationString (rqptr, &acptr->ResponseDuration[HTTP12],
                                             acptr->ResponseDurationCount[HTTP12]);
   *vecptr++ = DurationString (rqptr, &acptr->ResponseDuration[HTTP12]);

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

   *vecptr++ = acptr->BytesPerSecondMax[HTTP12];
   *vecptr++ = &acptr->BytesPerSecondMaxBytes[HTTP12];
   *vecptr++ = DurationString (rqptr,
                               &acptr->BytesPerSecondMaxDuration[HTTP12]);

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

   /* 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->DoDECnetCount;
   *vecptr++ = acptr->DoDECnetCount -
               acptr->DoDECnetCgiCount -
               acptr->DoDECnetOsuCount;
   *vecptr++ = acptr->DoDECnetCgiCount;
   *vecptr++ = acptr->DoDECnetOsuCount;

   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) */
      unsigned long  ResponseBytes [QUAD2];

      vecptr = FaoVector;

      *vecptr++ = GzipAvailable;
      *vecptr++ = acptr->GzipDeflateCount;
      *vecptr++ = PercentOf (acptr->GzipDeflateCount,
                             acptr->ProcessingTotalCount[HTTP12]);
      *vecptr++ = &acptr->GzipDeflateBytesIn;
      *vecptr++ = &acptr->GzipDeflateBytesOut;
      *vecptr++ = QuadPercentOf (&acptr->GzipDeflateBytesOut,
                                 &acptr->GzipDeflateBytesIn);

      *vecptr++ = acptr->GzipInflateCount;
      *vecptr++ = PercentOf (acptr->GzipInflateCount,
                             acptr->ProcessingTotalCount[HTTP12]);
      *vecptr++ = &acptr->GzipInflateBytesIn;
      *vecptr++ = &acptr->GzipInflateBytesOut;
      *vecptr++ = QuadPercentOf (&acptr->GzipInflateBytesIn,
                                 &acptr->GzipInflateBytesOut);

      PLUS_QUAD_QUAD (acptr->GzipDeflateBytesIn,
                      acptr->BytesRawTx,
                      ResponseBytes);
      SUB_QUAD_QUAD (acptr->GzipDeflateBytesOut, ResponseBytes);

      *vecptr++ = acptr->ProcessingTotalCount[HTTP12];
      *vecptr++ = &ResponseBytes;
      *vecptr++ = &acptr->BytesRawTx;
      *vecptr++ = QuadPercentOf (&acptr->BytesRawTx, &ResponseBytes);

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

   /* lookup and other */
   vecptr = FaoVector;

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

   *vecptr++ = acptr->ZeroedCount;
#if ODS_DIRECT
   *vecptr++ = OdsDirectReport (rqptr);
#else
   *vecptr++ = "";
#endif
   *vecptr++ = HttpdAlignFaultCount;
   *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;
      }
      else
         *vecptr++ = "";
      *vecptr++ = ")";
   }
   else
   {
      *vecptr++ = "";
      *vecptr++ = "";
      *vecptr++ = "";
   }

   *vecptr++ = acptr->SpuriousWakeCount;

   InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);

   FaoCheck (sizeof(FaoVector), &FaoVector, vecptr, FI_LI);
   status = FaolToNet (rqptr, LookupFao, &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 AdminServerProcessInput (REQUEST_STRUCT *rqptr)

{
   int  status;

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

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

   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 AdminServerProcessOutput (REQUEST_STRUCT *rqptr)

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

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

   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");
}

/*****************************************************************************/
/*
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>";
      *vecptr++ = cptr = rqptr->rqHeader.PathInfoPtr;
      *vecptr++ = cptr;
   }

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

   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 will 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 UseServerDatabase
)
{
   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++ = UseServerDatabase;
   *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);
}

/*****************************************************************************/
/*
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 ()

{
    static char  *JsRefresh =
"<div id=\"wasdRefreshDiv\" style=\"display:none;\">\n\
<p>Refresh:&nbsp;<select id=\"refreshInterval\" \
onfocus=\"selfoc=true\" onblur=\"selfoc=false\" \
onchange=\"selfoc=false;refreshNow()\">\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 language=\"JavaScript\">\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\
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 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";

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

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

   return (JsRefresh);
}

/*****************************************************************************/
/*
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\
\
input[type=submit]:disabled, input[type=reset]:disabled \
{ font-style:italic; }\n\
\
.bttn090 { min-width:0.9em!important; }\n\
.bttn125 { min-width:1.25em!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\
.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 { font-style:italic; color:#696969; }\n\
\
.alnk { }\n\
.hlght { background-color:#ededed; }\n\
.lftop th,td { padding:0.1em 0.2em 0 0.3em; text-align:left; }\n\
.pdlft1 { padding-left:1em; }\n\
.pdrght1 { padding-right:1em; }\n\
.pshlft { background-color:#ffffff; width:99%; }\n\
.px1 { margin:1px; }\n\
.sbttl { padding-bottom:0.3em!important; font-weight:bold; \
text-decoration:underline; }\n\
.tacnt { text-align:center!important; }\n\
.talft { text-align:left!important; }\n\
.targht { text-align:right!important; }\n\
\
</style>\n";

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

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

   return (WasdCss);
}

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

