/*****************************************************************************/
/*
                                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
---------------
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 NetWriteFaol(), 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_V6
#undef _VMS_V6_SOURCE
#define _VMS_V6_SOURCE
#undef __VMS_VER
#define __VMS_VER 60000000
#undef __CRTL_VER
#define __CRTL_VER 60000000
#endif

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

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

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

#define WASD_MODULE "ADMIN"

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

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

#ifdef DBUG
extern BOOL Debug;
#else
#define Debug 0 
#endif

extern BOOL  CacheEnabled,
             DclPersonaServicesAvailable,
             LoggingEnabled,
             OperateWithSysPrv,
             PersonaMacro,
             ProtocolHttpsConfigured,
             SesolaVerifyCAConfigured;

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

extern unsigned long  HttpdStartBinTime[];

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

extern ACCOUNTING_STRUCT  *AccountingPtr;
extern ACTIVITY_GBLSEC  *ActivityGblSecPtr;
extern CONFIG_STRUCT  Config;
extern HTTPD_GBLSEC  *HttpdGblSecPtr;
extern HTTPD_PROCESS  HttpdProcess;
extern MSG_STRUCT  Msgs;
extern MAPPING_META  *MappingMetaPtr;
extern PROXY_ACCOUNTING_STRUCT  *ProxyAccountingPtr;
extern SERVICE_STRUCT  *ServiceListHead;
extern SYS_INFO  SysInfo;
extern TCPIP_HOST_CACHE  *TcpIpHostCachePtr;
extern WATCH_STRUCT  Watch;

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

AdminBegin
(
REQUEST_STRUCT *rqptr,
REQUEST_AST NextTaskFunction
)
{
   BOOL  ClusterDo,
         UseServerDatabase;
   int  status;
   unsigned short  Length;
   char  *cptr;
   char  DoThis [128],
         FieldName [128],
         FieldValue [256],
         FilePath [ODS_MAX_FILE_NAME_LENGTH+1],
         Md5HashHexString [33],
         NumberString [16],
         Path [ODS_MAX_FILE_NAME_LENGTH+1],
         ProcessIdString [16],
         ProcessIdUserName [13],
         VirtualHostPort [128];

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

   if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_ADMIN))
      WatchThis (rqptr, FI_LI, WATCH_MOD_ADMIN,
                 "AdminBegin() !&A", NextTaskFunction);

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

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

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

   /* need to make some accomodations for our old friend Netscape 3.03/Gold */
   if (!memcmp (rqptr->rqHeader.UserAgentPtr, "Mozilla/3.", 10) &&
       strstr (rqptr->rqHeader.UserAgentPtr, "VMS"))
      rqptr->rqHeader.AdminNetscapeGold = true;
   else
      rqptr->rqHeader.AdminNetscapeGold = false;

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

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

#ifdef NOT_FULLY_IMPLEMENTED_YET

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

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

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

   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_VS_CONTROL,
                sizeof(ADMIN_VS_CONTROL)-1))
   {
      AdminControl (rqptr, NextTaskFunction, 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)))
         SysDclAst (NextTaskFunction, rqptr);
      else
         HTAdminBegin (rqptr, NextTaskFunction);
      return;
   }

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

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

   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_ACTIVITY, -1))
   {
      GraphActivityReport (rqptr, NextTaskFunction);
      return;
   }

   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_GRAPHIC_ACTIVITY, -1))
   {
      GraphActivityPlotBegin (rqptr, NextTaskFunction);
      return;
   }

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

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

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

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

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

      WatchDeleteProcess (rqptr, NextTaskFunction);
      return;
   }

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

   ClusterDo = false;
   UseServerDatabase = true;
   DoThis[0] = FilePath[0] = Md5HashHexString[0] = NumberString[0] =
      Path[0] = ProcessIdString[0] = ProcessIdUserName[0] =
      VirtualHostPort[0] = '\0';

   cptr = rqptr->rqHeader.QueryStringPtr;
   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);
         SysDclAst (NextTaskFunction, rqptr);
         return;
      }

      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 (toupper(FieldValue[0]) == 'Y')
            UseServerDatabase = false;
         else
         if (toupper(FieldValue[0]) == 'N')
            UseServerDatabase = true;
      }
      else
      if (strsame (FieldName, "virtual", -1))
         strzcpy (VirtualHostPort, FieldValue, sizeof(VirtualHostPort));
      else
      if (strsame (FieldName, "service", -1))
         strzcpy (VirtualHostPort, FieldValue, sizeof(VirtualHostPort));
      else
      if (strsame (FieldName, "server", -1))
      {
         if (toupper(FieldValue[0]) == 'Y')
            UseServerDatabase = true;
         else
         if (toupper(FieldValue[0]) == 'N')
            UseServerDatabase = false;
      }
      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, "at", -1) ||
          strsame (FieldName, "this", -1))
         strzcpy (NumberString, FieldValue, sizeof(NumberString));
      else
      if (strsame (FieldName, "scope", -1))
      {
         if (strsame (FieldValue, "cluster", -1)) ClusterDo = true;
      }
      else
      {
         rqptr->rqResponse.HttpStatus = 400;
         ErrorGeneral (rqptr, "Unknown query field.", FI_LI);
         SysDclAst (NextTaskFunction, rqptr);
         return;
      }
   }

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

   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, NextTaskFunction);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_AUTH_USER, -1))
   {
      AuthCacheReport (rqptr, NextTaskFunction);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_CACHE, -1))
   {
      CacheReport (rqptr, NextTaskFunction, false);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_CACHE_ENTRIES, -1))
   {
      if (Md5HashHexString[0])
         CacheReportEntry (rqptr, NextTaskFunction, Md5HashHexString);
      else
         CacheReport (rqptr, NextTaskFunction, true);
      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)))
         SysDclAst (NextTaskFunction, rqptr);
      else
      if (rqptr->rqHeader.Method == HTTP_METHOD_POST)
         PutBegin (rqptr, NextTaskFunction, NULL);
      else
      if (rqptr->rqHeader.QueryStringLength)
      {
         if (strsame (DoThis, "edit", -1))
            UpdBegin (rqptr, NextTaskFunction);
         else
         if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_CONFIG, -1))
            ConfigReport (rqptr, NextTaskFunction, UseServerDatabase);
         else
            ConfigRevise (rqptr, NextTaskFunction, UseServerDatabase);
      }
      else
         FileBegin (rqptr, NextTaskFunction, NULL, NULL,
                    rqptr->ParseOds.ExpFileName, "text/plain");
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_DCL, -1))
   {
      DclScriptingReport (rqptr, NextTaskFunction);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_HOSTCACHE, -1))
   {
      TcpIpHostCacheReport (rqptr, NextTaskFunction);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_PROCESS, -1))
   {
      WatchShowProcess (rqptr, NextTaskFunction,
                        ProcessIdString, ProcessIdUserName);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_DECNET, -1))
   {
      DECnetScriptingReport (rqptr, NextTaskFunction);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_LOCK, -1))
   {
      InstanceLockReport (rqptr, NextTaskFunction);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_PROCESS_INPUT, -1))
   {
      AdminServerProcessInput (rqptr, NextTaskFunction);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_PROCESS_OUTPUT, -1))
   {
      AdminServerProcessOutput (rqptr, NextTaskFunction);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_MEMORY, -1))
   {
      VmReport (rqptr, NextTaskFunction);
      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)))
         SysDclAst (NextTaskFunction, rqptr);
      else
      if (rqptr->rqHeader.Method == HTTP_METHOD_POST)
         PutBegin (rqptr, NextTaskFunction, NULL);
      else
      if (rqptr->rqHeader.QueryStringLength)
      {
         if (strsame (DoThis, "edit", -1))
            UpdBegin (rqptr, NextTaskFunction);
         else
         if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_MESSAGES, -1))
            MsgConfigReport (rqptr, NextTaskFunction, UseServerDatabase);
         else
            MsgConfigRevise (rqptr, NextTaskFunction, UseServerDatabase);
      }
      else
         FileBegin (rqptr, NextTaskFunction, 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)))
         SysDclAst (NextTaskFunction, rqptr);
      else
      if (rqptr->rqHeader.Method == HTTP_METHOD_POST)
         PutBegin (rqptr, NextTaskFunction, NULL);
      else
      if (rqptr->rqHeader.QueryStringLength)
         if (strsame (DoThis, "edit", -1))
            UpdBegin (rqptr, NextTaskFunction);
         else
            AuthConfigReport (rqptr, NextTaskFunction,
                              UseServerDatabase, VirtualHostPort);
      else
         FileBegin (rqptr, NextTaskFunction, NULL, NULL,
                    rqptr->ParseOds.ExpFileName, "text/plain");
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_PROXY, -1))
   {
      ProxyMaintReport (rqptr, NextTaskFunction);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_REQUEST, -1))
   {
      RequestReport (rqptr, NextTaskFunction, false);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_REQUEST_HISTORY, -1))
   {
      RequestReport (rqptr, NextTaskFunction, true);
      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)))
         SysDclAst (NextTaskFunction, rqptr);
      else
      if (rqptr->rqHeader.Method == HTTP_METHOD_POST)
         PutBegin (rqptr, NextTaskFunction, NULL);
      else
      if (rqptr->rqHeader.QueryStringLength)
      {
         if (strsame (DoThis, "edit", -1))
            UpdBegin (rqptr, NextTaskFunction);
         else
         if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_SERVICES, -1))
            ServiceReport (rqptr, NextTaskFunction, UseServerDatabase);
         else
            ServiceConfigRevise (rqptr, NextTaskFunction, UseServerDatabase);
      }
      else
         FileBegin (rqptr, NextTaskFunction, NULL, NULL,
                    rqptr->ParseOds.ExpFileName, "text/plain");
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_SERVICE_LOG, -1))
   {
      LoggingReport (rqptr, &RequestEnd, VirtualHostPort);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_SHOW_PROCESS, -1))
   {
      WatchShowProcess (rqptr, NextTaskFunction,
                        ProcessIdString, ProcessIdUserName);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_STATS, -1))
   {
      AdminReportServerStats (rqptr, NextTaskFunction);
      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)))
         SysDclAst (NextTaskFunction, rqptr);
      else
      if (rqptr->rqHeader.Method == HTTP_METHOD_POST)
         PutBegin (rqptr, NextTaskFunction, NULL);
      else
      if (rqptr->rqHeader.QueryStringLength)
         if (strsame (DoThis, "edit", -1))
            UpdBegin (rqptr, NextTaskFunction);
         else
            MapUrl_Report (rqptr, NextTaskFunction,
                           UseServerDatabase, VirtualHostPort);
      else
         FileBegin (rqptr, NextTaskFunction, 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, NextTaskFunction, 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)))
         SysDclAst (NextTaskFunction, rqptr);
      else
      if (rqptr->rqHeader.Method == HTTP_METHOD_POST)
         PutBegin (rqptr, NextTaskFunction, NULL);
      else
      if (strsame (DoThis, "edit", -1))
         UpdBegin (rqptr, NextTaskFunction);
      else
      if (strsame (DoThis, "report", -1))
         SesolaReportCA (rqptr, NextTaskFunction, VirtualHostPort);
      else
         FileBegin (rqptr, NextTaskFunction, NULL, NULL,
                    rqptr->ParseOds.ExpFileName, "text/plain");
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_SUPERVISOR, -1))
   {
      /* this does not appear in the menu anymore but is still available */
      HttpdSupervisorReport (rqptr, NextTaskFunction);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_SYSTEM, -1))
   {
      WatchShowSystem (rqptr, NextTaskFunction);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_REPORT_THROTTLE, -1))
   {
      ThrottleReport (rqptr, NextTaskFunction);
      return;
   }
   else
   if (strsame (rqptr->rqHeader.PathInfoPtr, ADMIN_CONTROL,
                sizeof(ADMIN_CONTROL)-1))
   {
      /******************/
      /* server control */
      /******************/

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

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

   rqptr->rqResponse.HttpStatus = 400;
   ErrorGeneral (rqptr, "Unknown function.", FI_LI);
   SysDclAst (NextTaskFunction, 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 (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_ADMIN))
      WatchThis (rqptr, FI_LI, WATCH_MOD_ADMIN, "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 = CONFIG_FILE_NAME;
   else
   if (strsame (cptr, ADMIN_REPORT_MESSAGES, -1) ||
       strsame (cptr, ADMIN_REVISE_MESSAGES, -1))
      sptr = CONFIG_MSG_FILE_NAME;
   else
   if (strsame (cptr, ADMIN_REPORT_AUTH_PATHS, -1) ||
       strsame (cptr, ADMIN_REVISE_AUTH_PATHS, -1))
      sptr = CONFIG_AUTH_FILE_NAME;
   else
   if (strsame (cptr, ADMIN_REPORT_MAPPING, -1) ||
       strsame (cptr, ADMIN_REVISE_MAPPING, -1))
      sptr = CONFIG_MAP_FILE_NAME;
   else
   if (strsame (cptr, ADMIN_REPORT_SERVICES, -1) ||
       strsame (cptr, ADMIN_REVISE_SERVICES, -1))
      sptr = CONFIG_SERVICE_FILE_NAME;
   else
   if (strsame (cptr, ADMIN_REVISE_SITELOG, -1) ||
       strsame (cptr, ADMIN_REVISE_SITELOG_ENTRY, -1))
   {
      if (getenv (CONFIG_SITELOG_FILE_NAME))
         sptr = CONFIG_SITELOG_FILE_NAME;
      else
         sptr = DEFAULT_SITELOG_FILE_NAME;
   }
   else
   if (strsame (cptr, ADMIN_REPORT_SSL_CA, -1) ||
       strsame (cptr, ADMIN_REVISE_SSL_CA, -1))
   {
      if (VirtualHostPort[0])
      {
         for (svptr = ServiceListHead; svptr; svptr = svptr->NextPtr)
            if (strsame (svptr->ServerHostPort, VirtualHostPort, -1)) break;
         if (!svptr) return (SS$_NOSUCHFILE);
         sptr = ((SESOLA_CONTEXT*)svptr->SSLserverPtr)->CaFilePtr;
      }
      else
         sptr = CONFIG_SSL_CAFILE;
   }
   else
   if (strsame (cptr, ADMIN_REVISE_HTA, sizeof(ADMIN_REVISE_HTA)-1))
   {
      /* server admin of HTA database(s) */
      zptr = (sptr = FileSpec) + sizeof(FileSpec)-1;
      if (rqptr->ConfigDirectoryLength)
         cptr = rqptr->ConfigDirectory;
      else
         cptr = HTA_DIRECTORY;
      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 = HTA_DIRECTORY;
      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);

   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_CATEGORY(WATCH_RESPONSE))
         WatchThis (rqptr, FI_LI, 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.
*/ 

AdminControl
(
REQUEST_STRUCT *rqptr,
REQUEST_AST NextTaskFunction,
BOOL ClusterDo
)
{
   int  cnt, status,
        LockIndex;
   char  *cptr, *sptr;
   char  String[32];

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

   if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_ADMIN))
      WatchThis (rqptr, FI_LI, WATCH_MOD_ADMIN, "AdminControl() !&A !UL !&Z",
                 NextTaskFunction, ClusterDo, rqptr->rqHeader.PathInfoPtr);

   cptr = rqptr->rqHeader.PathInfoPtr;

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

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

   sptr = NULL;
   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_INSTANCE,
                sizeof(ADMIN_CONTROL_INSTANCE)-1))
   {
      sptr = cptr;
      while (*cptr) cptr++;
      while (cptr > sptr && *cptr != '/') cptr--;
      if (*cptr == '/')
      {
         cptr++;
         WriteFao (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;
   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_THROTTLE_ZERO, -1))
      sptr = CONTROL_THROTTLE_ZERO;
   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);
      SysDclAst (NextTaskFunction, rqptr);
      return;
   }

   /* this must be completely asynchronous as we're in an AST here! */
   status = InstanceLockControl ();
   if (status == SS$_NOTQUEUED)
   {
      rqptr->rqResponse.HttpStatus = 403;
      ErrorGeneral (rqptr, "In use elsewhere!!", FI_LI);
      SysDclAst (NextTaskFunction, rqptr);
      return;
   }
   if (VMSnok (status))
   {
      rqptr->rqResponse.ErrorTextPtr = "sys$enqw()";
      ErrorVmsStatus (rqptr, status, FI_LI);
      SysDclAst (NextTaskFunction, rqptr);
      return;
   }
   if (ClusterDo)
      LockIndex = INSTANCE_CLUSTER_DO;
   else
      LockIndex = INSTANCE_NODE_DO;
   cptr = ControlEnqueueCommand (LockIndex, sptr);
   InstanceUnLockControl ();
   if (cptr[0] == '!')
   {
      rqptr->rqResponse.HttpStatus = 403;
      ErrorGeneral (rqptr, cptr+1, FI_LI);
      SysDclAst (NextTaskFunction, rqptr);
      return;
   }

   ReportSuccess (rqptr,
"The directive &quot;!AZ&quot; has been (en)queued, with !AZ.",
         sptr, cptr);
   SysDclAst (NextTaskFunction, rqptr);
}

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

AdminMenu
(
REQUEST_STRUCT *rqptr,
REQUEST_AST NextTaskFunction
)
{
   char  BeginPageFao [] =
"<HTML>\n\
<HEAD>\n\
!AZ\
<TITLE>HTTPd !AZ ... Server Administration</TITLE>\n\
<SCRIPT LANGUAGE=\"JavaScript\">\n\
<!!--\n\
function doIt(link,doing) {\n\
!&@\
   if (doing == \"restart\") return \
confirm (\"Restart \" + scope + \" after allowing current requests to complete?\");\n\
   if (doing == \"restartNow\") return \
confirm (\"Restart \" + scope + \" DISCONNECTING current requests?\");\n\
   if (doing == \"restartQuiet\") return \
confirm (\"Restart \" + scope + \" when current requests reach zero?\");\n\
   if (doing == \"exit\") return \
confirm (\"Exit \" + scope + \" allowing current requests to complete?\");\n\
   if (doing == \"exitNow\") return \
confirm (\"Exit \" + scope + \" DISCONNECTING current requests?\");\n\
   return true;\n\
}\n\
// -->\n\
</SCRIPT>\n\
</HEAD>\n\
!AZ\n\
<H2><NOBR>HTTPd !AZ</NOBR></H2>\n\
<H3>Server Administration!&@</H3>\n\
<P>\n\
\
<FORM NAME=doallForm>\n\
\
<TABLE CELLPADDING=5 CELLSPACING=0 BORDER=1>\n\
\
<TR><TH COLSPAN=2>Configuration</TH><TR>\n\
\
<TD COLSPAN=2 ALIGN=center VALIGN=top>\n\
\
<TABLE CELLPADDING=3 CELLSPACING=0 BORDER=0>\n\
\
<TR><TD></TD><TD>|</TD>\
<TH COLSPAN=2>Report</TH><TD>|</TD>\n\
<TH COLSPAN=3>Revise</TH><TD>|</TD>\n\
<TH>Action</TH><TD>|</TD><TR>\n\
\
<TR>\n\
<TH>Server</TH>\n\
<TD>&nbsp;</TD>\n\
<TD>[<A HREF=\"!AZ\">Statistics</A>]</TD>\n\
<TD>[!&@]</TD>\n\
<TD>&nbsp;</TD>\n\
<TD COLSPAN=2>[<A HREF=\"!AZ?do=edit\">Site-Log</A>]</TD>\n\
<TD>[<A HREF=\"!AZ?do=edit\">Edit</A>]</TD>\n\
<TD>&nbsp;</TD>\n\
<TD>[<A HREF=\"!AZ\" onClick=\"return doIt(this,null)\">Zero</A>]</TD>\n\
</TR>\n\
\
<TR>\n\
<TH>Configuration</TH>\n\
<TD>&nbsp;</TD>\n\
<TD>[<A HREF=\"!AZ?server=yes\">Server</A>]</TD>\n\
<TD>[<A HREF=\"!AZ?file=yes\">File</A>]</TD>\n\
<TD>&nbsp;</TD>\n\
<TD>[<A HREF=\"!AZ?server=yes\">Server</A>]</TD>\n\
<TD>[<A HREF=\"!AZ?file=yes\">File</A>]</TD>\n\
<TD>[<A HREF=\"!AZ?do=edit\">Edit</A>]</TD>\n\
</TR>\n\
\
<TR>\n\
<TH>Services</TH>\n\
<TD>&nbsp;</TD>\n\
<TD>[<A HREF=\"!AZ?server=yes\">Server</A>]</TD>\n\
<TD>[<A HREF=\"!AZ?file=yes\">File</A>]</TD>\n\
<TD>&nbsp;</TD>\n\
<TD>[<A HREF=\"!AZ?server=yes\">Server</A>]</TD>\n\
<TD>[<A HREF=\"!AZ?file=yes\">File</A>]</TD>\n\
<TD>[<A HREF=\"!AZ?do=edit\">Edit</A>]</TD>\n\
</TR>\n\
\
<TR>\n\
<TH>Messages</TH>\n\
<TD>&nbsp;</TD>\n\
<TD>[<A HREF=\"!AZ?server=yes\">Server</A>]</TD>\n\
<TD>[<A HREF=\"!AZ?file=yes\">File</A>]</TD>\n\
<TD>&nbsp;</TD>\n\
<TD>[<A HREF=\"!AZ?server=yes\">Server</A>]</TD>\n\
<TD>[<A HREF=\"!AZ?file=yes\">File</A>]</TD>\n\
<TD>[<A HREF=\"!AZ?do=edit\">Edit</A>]</TD>\n\
</TR>\n\
\
<TR>\n\
<TH>Path&nbsp;Mapping</TH>\n\
<TD>&nbsp;</TD>\n\
<TD>[<A HREF=\"!AZ?server=yes\">Server</A>]</TD>\n\
<TD>[<A HREF=\"!AZ?file=yes\">File</A>]</TD>\n\
<TD>&nbsp;</TD>\n\
<TD>&nbsp;</TD>\n\
<TD>&nbsp;</TD>\n\
<TD>[<A HREF=\"!AZ?do=edit\">Edit</A>]</TD>\n\
<TD>&nbsp;</TD>\n\
<TD>[<A HREF=\"!AZ\" onClick=\"return doIt(this,null)\">Reload</A>]</TD>\n\
</TR>\n\
\
<TR>\n\
<TH>Path&nbsp;Authorization</TH>\n\
<TD>&nbsp;</TD>\n\
<TD>[<A HREF=\"!AZ?server=yes\">Server</A>]</TD>\n\
<TD>[<A HREF=\"!AZ?file=yes\">File</A>]</TD>\n\
<TD>&nbsp;</TD>\n\
<TD>&nbsp;</TD>\n\
<TD>&nbsp;</TD>\n\
<TD>[<A HREF=\"!AZ?do=edit\">Edit</A>]</TD>\n\
<TD>&nbsp;</TD>\n\
<TD>[<A HREF=\"!AZ\" onClick=\"return doIt(this,null)\">Reload</A>]</TD>\n\
</TR>\n\
\
<TR>\n\
<TH>User&nbsp;Authentication</TH>\n\
<TD>&nbsp;</TD>\n\
<TD>[<A HREF=\"!AZ\">Server</A>]</TD>\n\
<TD>&nbsp;</TD>\n\
<TD>&nbsp;</TD>\n\
<TD COLSPAN=2>[<A HREF=\"!AZ\">HTA</A>]&nbsp;&nbsp;\
[<A HREF=\"!AZ\">HTL</A>]</TD>\n\
<TD>&nbsp;</TD>\n\
<TD>&nbsp;</TD>\n\
<TD>[<A HREF=\"!AZ\" onClick=\"return doIt(this,null)\">Purge</A>]</TD>\n\
</TR>\n\
\
<TR>\n\
<TH>Secure&nbsp;Sockets</TH>\n\
<TD>&nbsp;</TD>\n\
<TD>[!&@]</TD>\n\
<TD>[!&@]</TD>\n\
<TD>&nbsp;</TD>\n\
<TD>&nbsp;</TD>\n\
<TD COLSPAN=2 ALIGN=right>[!&@]</TD>\n\
<TD>&nbsp;</TD>\n\
<TD>[!&@]</TD>\n\
</TR>\n\
\
<TR>\n\
<TH>Other&nbsp;Reports</TH>\n\
<TD>&nbsp;</TD>\n\
<TD COLSPAN=11><NOBR>\n\
[<A HREF=\"!AZ\">Cache</A>]\n\
[<A HREF=\"!AZ\">DCL</A>]\n\
[<A HREF=\"!AZ\">DECnet</A>]\n\
[!&@]\n\
[<A HREF=\"!AZ\">Lock</A>]\n\
[<A HREF=\"!AZ\">Match</A>]\n\
[<A HREF=\"!AZ\">Memory</A>]\n\
<BR>\n\
[<A HREF=\"!AZ\">Process</A>]\n\
[!&@]\n\
[<A HREF=\"!AZ\">Request</A>]\n\
[<A HREF=\"!AZ\">System</A>]\n\
[!&@]\n\
[!&@]\n\
<BR>\n\
[!&@!&@!&@!&@!&@!&@!&@!&@!&@!&@!&@<FONT SIZE=-1>hours activity</FONT>]\n\
</NOBR></TD></TR>\n\
\
</TABLE>\n\
\
<TR>\n\
<TH><FONT COLOR=\"#ff0000\">Control</FONT></TH>\n\
\
<TD ROWSPAN=2 ALIGN=CENTER VALIGN=top>\n\
\
<TABLE CELLPADDING=3 CELLSPACING=0 BORDER=0>\n\
<TR><TH COLSPAN=4 ALIGN=center VALIGN=top>\
<FONT SIZE=-1><NOBR>!20&W</NOBR></FONT></TH></TR>\n\
<TR><TD COLSPAN=4 ALIGN=center VALIGN=top>\
<FONT SIZE=-2><NOBR>!AZ (!AZ)</NOBR></FONT></TD></TR>\n\
\
<TR><TH></TH></TR>\n\
\
<TR><TH ROWSPAN=4 ALIGN=left VALIGN=top><FONT SIZE=-1>Times</FONT></TH>\n\
\
<TH ALIGN=right><FONT SIZE=-1>!AZ::</FONT></TH>\
<TD align=right><FONT SIZE=-1><NOBR>!%D</NOBR></FONT></TD></TR>\n\
\
<TR><TH ALIGN=right><FONT SIZE=-1>!&@:</FONT></TH>\
<TD align=right><FONT SIZE=-1><NOBR>!%D</NOBR></FONT></TD></TR>\n\
\
<TR><TH ALIGN=right><FONT SIZE=-1>Image:</FONT></TH>\
<TD align=right><FONT SIZE=-1><NOBR>!%D</NOBR></FONT></TD></TR>\n\
\
<TR><TH ALIGN=right><FONT SIZE=-1>CPU:</FONT></TH>\
<TD align=right><FONT SIZE=-1><NOBR>!UL !2ZL:!2ZL:!2ZL.!2ZL</NOBR></FONT></TD></TR>\n\
\
<TR><TH ROWSPAN=2 ALIGN=left VALIGN=top><FONT SIZE=-1>Requests&nbsp;</FONT></TH>\n\
<TH ALIGN=right><FONT SIZE=-1>Read:</FONT></TH>\
<TD align=right><FONT SIZE=-1>!&,UL</FONT></TD></TR>\n\
\
<TR><TH ALIGN=right><FONT SIZE=-1>Write:</FONT></TH>\
<TD align=right><FONT SIZE=-1>!&,UL</FONT></TD></TR>\n\
\
<TR><TH ROWSPAN=2 ALIGN=left VALIGN=top><FONT SIZE=-1>Bytes</FONT></TH>\n\
<TH ALIGN=right><FONT SIZE=-1>Rx:</FONT></TH>\
<TD align=right><FONT SIZE=-1>!&,@SQ</FONT></TD></TR>\n\
\
<TR><TH ALIGN=right><FONT SIZE=-1>Tx:</FONT></TH>\
<TD align=right><FONT SIZE=-1>!&,@SQ</FONT></TD></TR>\n\
!&@\
</TABLE>\n\
\
</TD></TR>\n\
<TR><TD VALIGN=top>\n\
\
<TABLE CELLPADDING=3 CELLSPACING=0 BORDER=0>\n\
<TR><TD VALIGN=top ALIGN=left>\n\
\
<TABLE CELLPADDING=3 CELLSPACING=0 BORDER=0>\n\
<TR><TD>[\
<A HREF=\"!AZ\" onClick=\"return doIt(this,\'restart\')\">RESTART</A>\
]</TD></TR>\n\
<TR><TD>[\
<A HREF=\"!AZ\" onClick=\"return doIt(this,\'restartNow\')\">\
<FONT SIZE=-1>RESTART</FONT>NOW</A>\
]</TD></TR>\n\
<TR><TD>[\
<A HREF=\"!AZ\" onClick=\"return doIt(this,\'restartQuiet\')\">\
<FONT SIZE=-1>RESTART</FONT>Quiet</A>\
]</TD></TR>\n\
<TR><TD>[\
<A HREF=\"!AZ\" onClick=\"return doIt(this,\'exit\')\">EXIT</A>\
]</TD></TR>\n\
<TR><TD>[\
<A HREF=\"!AZ\" onClick=\"return doIt(this,\'exitNow\')\">\
<FONT SIZE=-1>EXIT</FONT>NOW</A>\
]</TD></TR>\n\
</TABLE>\n\
\
</TD><TD>&nbsp;</TD><TD VALIGN=top>\n\
\
<TABLE CELLPADDING=3 CELLSPACING=0 BORDER=0>\n\
<TR><TD ALIGN=right><B>Log</B></TD><TD>!&@</TD></TR>\n\
<TR><TD ALIGN=right><B>Cache</B></TD><TD>!&@</TD></TR>\n\
<TR><TD ALIGN=right><B>Proxy</B></TD><TD>!&@</TD></TR>\n\
<TR><TD ALIGN=right VALIGN=top><B>Instance</B></TD><TD><NOBR>[\
!&@\
!&@";

   char  EndInstanceFao [] =
"]</NOBR></TD></TR>\n\
</TABLE>\n\
\
</TD></TR>\n";

   char  EndPageFao [] =
"</TABLE>\n\
\
</TD></TR>\n\
</TABLE>\n\
\
</FORM>\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 [2],
                         CurrentTime [2],
                         ImageUpTime [2],
                         JpiLoginTime [2],
                         ProcessUpTime [2],
                         SystemUpTime [2];
   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, status,
        ClusterInstanceCount,
        InstanceStartupMax,
        NodeInstanceCount;
   unsigned short  Length;
   unsigned long  ActivityDeltaHours;
   unsigned long  *vecptr;
   unsigned long  FaoVector [192];
   char  *cptr,
         *ClusterInstancePtr,
         *NodeInstancePtr;
   IO_SB  IOsb;

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

   if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_ADMIN))
      WatchThis (rqptr, FI_LI, WATCH_MOD_ADMIN,
                 "AdminMenu() !&A", NextTaskFunction);

   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);
      SysDclAst (NextTaskFunction, 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);
      SysDclAst (NextTaskFunction, rqptr);
      return;
   }

   sys$gettim (&CurrentTime);
   lib$sub_times (&CurrentTime, &SysInfo.BootBinTime, &SystemUpTime);
   lib$sub_times (&CurrentTime, &HttpdStartBinTime, &ImageUpTime);
   lib$sub_times (&CurrentTime, &JpiLoginTime, &ProcessUpTime);

   if (ActivityTotalMinutes)
   {
      InstanceMutexLock (INSTANCE_MUTEX_HTTPD);
      lib$sub_times (&CurrentTime, &ActivityGblSecPtr->StartBinTime,
                     &ActivityUpTime);
      InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);
      lib$cvt_from_internal_time (&LibDeltaHours, &ActivityDeltaHours,
                                  &ActivityUpTime);
   }
   else
      ActivityDeltaHours = 0;
   if (Debug) fprintf (stdout, "ActivityDeltaHours: %d\n", ActivityDeltaHours);

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

   rqptr->rqResponse.PreExpired = PRE_EXPIRE_ADMIN;
   RESPONSE_HEADER_200_HTML (rqptr);

   vecptr = FaoVector;

   *vecptr++ = HtmlMetaInfo (rqptr, NULL);
   *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++ = Config.cfServer.AdminBodyTag;
   *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 HREF=\"!AZ\">Log</A>";
      *vecptr++ = ADMIN_REPORT_PROCESS_OUTPUT;
   }
   else
      *vecptr++ = "Log";

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

   /* 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 &&
       rqptr->ServicePtr->RequestScheme == SCHEME_HTTPS)
   {
      *vecptr++ = "<A HREF=\"!AZ\">Service</A>";
      *vecptr++ = ADMIN_REPORT_SSL;
      if (SesolaVerifyCAConfigured)
      {
         *vecptr++ = "<A HREF=\"!AZ?do=report&virtual=!AZ\">CA</A>";
         *vecptr++ = ADMIN_REPORT_SSL_CA;
         *vecptr++ = rqptr->ServicePtr->ServerHostPort;
         *vecptr++ = "<A HREF=\"!AZ?do=edit\">Edit-CA</A>";
         *vecptr++ = ADMIN_REVISE_SSL_CA;
         *vecptr++ =
"<A HREF=\"!AZ\" onClick=\"return doIt(this,null)\">Reload-CA</A>";
         *vecptr++ = ADMIN_CONTROL_SSL_CA_LOAD;
      }
      else
      {
         *vecptr++ = "CA";
         *vecptr++ = "Edit-CA";
         *vecptr++ = "Reload-CA";
      }
   }
   else
   {
      *vecptr++ = "Service";
      *vecptr++ = "CA";
      *vecptr++ = "Edit-CA";
      *vecptr++ = "Reload-CA";
   }

   /* other */
   *vecptr++ = ADMIN_REPORT_CACHE;
   *vecptr++ = ADMIN_REPORT_DCL;
   *vecptr++ = ADMIN_REPORT_DECNET;

   if (TcpIpHostCachePtr)
   {
      *vecptr++ = "<A HREF=\"!AZ\">Host</A>";
      *vecptr++ = ADMIN_REPORT_HOSTCACHE;
   }
   else
      *vecptr++ = "Host";

   *vecptr++ = ADMIN_REPORT_LOCK;

   *vecptr++ = ADMIN_REPORT_MATCH;
   *vecptr++ = ADMIN_REPORT_MEMORY;
   *vecptr++ = ADMIN_REPORT_PROCESS;

   if (ProxyServiceCount)
   {
      *vecptr++ = "<A HREF=\"!AZ\">Proxy</A>";
      *vecptr++ = ADMIN_REPORT_PROXY;
   }
   else
      *vecptr++ = "Proxy";

   /* request and system */
   *vecptr++ = ADMIN_REPORT_REQUEST;
   *vecptr++ = ADMIN_REPORT_SYSTEM;

   /* throttle */
   if (!MappingMetaPtr->ThrottleTotal)
      *vecptr++ = "Throttle";
   else
   {
      *vecptr++ = "<A HREF=\"!AZ\">Throttle</A>";
      *vecptr++ = ADMIN_REPORT_THROTTLE;
   }

   /* WATCH facility */
   if (!WATCH_CAT || Watch.Disabled)
      *vecptr++ = "WATCH";
   else
   {
      *vecptr++ = "<A HREF=\"!AZ\">WATCH</A>";
      *vecptr++ = ADMIN_REPORT_WATCH;
   }

   /* activity */
   if (ActivityTotalMinutes)
   {
      *vecptr++ = "<A HREF=\"!AZ?1\">1</A>,\n";
      *vecptr++ = ADMIN_REPORT_ACTIVITY;
   }
   else
      *vecptr++ = "1, ";
   if (ActivityDeltaHours >= 1)
   {
      *vecptr++ = "<A HREF=\"!AZ?2\">2</A>,\n";
      *vecptr++ = ADMIN_REPORT_ACTIVITY;
   }
   else
      *vecptr++ = "2, ";
   if (ActivityDeltaHours >= 2)
   {
      *vecptr++ = "<A HREF=\"!AZ?4\">4</A>,\n";
      *vecptr++ = ADMIN_REPORT_ACTIVITY;
   }
   else
      *vecptr++ = "4, ";
   if (ActivityDeltaHours >= 4)
   {
      *vecptr++ = "<A HREF=\"!AZ?8\">8</A>,\n";
      *vecptr++ = ADMIN_REPORT_ACTIVITY;
   }
   else
      *vecptr++ = "8, ";
   if (ActivityDeltaHours >= 8)
   {
      *vecptr++ = "<A HREF=\"!AZ?16\">16</A>,\n";
      *vecptr++ = ADMIN_REPORT_ACTIVITY;
   }
   else
      *vecptr++ = "16, ";
   if (ActivityDeltaHours >= 16)
   {
      *vecptr++ = "<A HREF=\"!AZ?24\">24</A>,\n";
      *vecptr++ = ADMIN_REPORT_ACTIVITY;
   }
   else
      *vecptr++ = "24, ";
   if (ActivityDeltaHours >= 24)
   {
      *vecptr++ = "<A HREF=\"!AZ?72\">72</A>,\n";
      *vecptr++ = ADMIN_REPORT_ACTIVITY;
   }
   else
      *vecptr++ = "72, ";
   if (ActivityDeltaHours >= 72)
   {
      *vecptr++ = "<A HREF=\"!AZ?168\">168</A>,\n";
      *vecptr++ = ADMIN_REPORT_ACTIVITY;
   }
   else
      *vecptr++ = "168, ";
   if (ActivityDeltaHours >= 168)
   {
      *vecptr++ = "<A HREF=\"!AZ?336\">336</A>,\n";
      *vecptr++ = ADMIN_REPORT_ACTIVITY;
   }
   else
      *vecptr++ = "336, ";
   if (ActivityDeltaHours >= 336)
   {
      *vecptr++ = "<A HREF=\"!AZ?504\">504</A>,\n";
      *vecptr++ = ADMIN_REPORT_ACTIVITY;
   }
   else
      *vecptr++ = "504, ";
   if (ActivityDeltaHours >= 504)
   {
      *vecptr++ = "<A HREF=\"!AZ?672\">672</A>\n";
      *vecptr++ = ADMIN_REPORT_ACTIVITY;
   }
   else
      *vecptr++ = "672 ";

   /* informational accounting */
   *vecptr++ = &rqptr->rqTime.Vms64bit;
   *vecptr++ = rqptr->rqTime.GmDateTime;
   *vecptr++ = TimeGmtString;
   *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++ = AccountingPtr->MethodGetCount +
               AccountingPtr->MethodHeadCount;
   *vecptr++ = AccountingPtr->MethodConnectCount +
               AccountingPtr->MethodDeleteCount +
               AccountingPtr->MethodPostCount +
               AccountingPtr->MethodPutCount;
   *vecptr++ = &AccountingPtr->QuadBytesRawRx;
   *vecptr++ = &AccountingPtr->QuadBytesRawTx;
   if (ClusterInstanceCount > 5)
      *vecptr++ =
"<TR><TD COLSPAN=4><HR SIZE=1 WIDTH=60% ALIGN=center NOSHADE></TD></TR>\n";
   else
      *vecptr++ = "";


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

   /* logging control menu items */
   if (LoggingEnabled)
   {
      *vecptr++ =
"<NOBR>[On] [<A HREF=\"!AZ\" onClick=\"return doIt(this,null)\">Off</A>] \
[<A HREF=\"!AZ\" onClick=\"doIt(this,null)\">Flush</A>]</NOBR>";
      *vecptr++ = ADMIN_CONTROL_LOG_CLOSE;
      *vecptr++ = ADMIN_CONTROL_LOG_FLUSH;
   }
   else
   {
      *vecptr++ =
"<NOBR>[<A HREF=\"!AZ\" onClick=\"return doIt(this,null)\">On</A>] \
[Off] [Flush]</NOBR>";
      *vecptr++ = ADMIN_CONTROL_LOG_OPEN;
   }

   /* cache control menu items */
   if (CacheEnabled)
   {
      *vecptr++ =
"<NOBR>[On] [<A HREF=\"!AZ\" onClick=\"return doIt(this,null)\">Off</A>] \
[<A HREF=\"!AZ\" onClick=\"return doIt(this,null)\">Purge</A>]</NOBR>";
      *vecptr++ = ADMIN_CONTROL_CACHE_OFF;
      *vecptr++ = ADMIN_CONTROL_CACHE_PURGE;
   }
   else
   {
      *vecptr++ =
"<NOBR>[<A HREF=\"!AZ\" onClick=\"return doIt(this,null)\">On</A>] \
[Off] [Purge]</NOBR>";
      *vecptr++ = ADMIN_CONTROL_CACHE_ON;
   }

   /* proxy control  */
   if (ProxyServiceCount)
   {
      *vecptr++ = "<NOBR>[<A HREF=\"!AZ\">Adjust</A>]</NOBR>";
      *vecptr++ = ADMIN_CONTROL_PROXY_ADJUST;
   }
   else
      *vecptr++ = "<NOBR>[Adjust]</NOBR>";

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

   if (InstanceStartupMax)
   {
      *vecptr++ =
"<A HREF=\"!AZ/max\" onClick=\"return doIt(this,null)\">max</A>,\n";
      *vecptr++ = ADMIN_CONTROL_INSTANCE;
   }
   else
      *vecptr++ = "max,\n";
   if (InstanceStartupMax != INSTANCE_PER_CPU)
   {
      *vecptr++ =
"<A HREF=\"!AZ/cpu\" onClick=\"return doIt(this,null)\">CPU</A>,\n";
      *vecptr++ = ADMIN_CONTROL_INSTANCE;
   }
   else
      *vecptr++ = "CPU,\n";

   status = NetWriteFaol (rqptr, BeginPageFao, &FaoVector);
   if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);

   for (cnt = 1; cnt <= INSTANCE_MAX; cnt++)
   {
      vecptr = FaoVector;
      if (cnt == InstanceStartupMax)
      {
         *vecptr++ = cnt;
         *vecptr++ = cnt < INSTANCE_MAX;
         status = NetWriteFaol (rqptr, "!UL!&?,\n\r\r", &FaoVector);
      }
      else
      {
         *vecptr++ = ADMIN_CONTROL_INSTANCE;
         *vecptr++ = cnt;
         *vecptr++ = cnt < INSTANCE_MAX;
         status = NetWriteFaol (rqptr,
"<A HREF=\"!AZ/!UL\" onClick=\"return doIt(this,null)\">!-!UL</A>!&?,\n\r\r",
                     &FaoVector);
      }
      if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);
      if (cnt == ((INSTANCE_MAX - 2) / 2) - 1)
         NetWriteFaol (rqptr, "<BR>\n&nbsp;", NULL);
   }

   status = NetWriteFaol (rqptr, EndInstanceFao, NULL);
   if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);

   if (SyiClusterNodes > 1 && ClusterInstanceCount > NodeInstanceCount)
   {
      cptr =
"<TR><TD COLSPAN=3 ALIGN=left>\n\
<TABLE CELLPADDING=3 CELLSPACING=0 BORDER=0>\n\
<TR><TH COLSPAN=2 ALIGN=left VALIGN=top>\
Node&nbsp;<INPUT TYPE=radio NAME=scope VALUE=node CHECKED>\
</TH><TH>&nbsp;</TH><TH COLSPAN=2 ALIGN=left VALIGN=top>\
Cluster&nbsp;<INPUT TYPE=radio NAME=scope VALUE=cluster>\
</TH></TR>\n\
<TR><TD>&nbsp;</TD><TD ALIGN=left VALIGN=top><FONT SIZE=-1>\n";
      status = NetWriteFaol (rqptr, cptr, NULL);
      if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);

      AdminMenuInstanceList (rqptr, NodeInstancePtr);

      cptr =
"</FONT></TD>\
<TD>&nbsp;</TD><TD>&nbsp;</TD>\n\
<TD ALIGN=left VALIGN=top><FONT SIZE=-1>\n";
      status = NetWriteFaol (rqptr, cptr, NULL);
      if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);

      AdminMenuInstanceList (rqptr, ClusterInstancePtr);

      cptr =
"</FONT></TD></TR>\n\
</TABLE>\n\
</TD></TR>\n";
      status = NetWriteFaol (rqptr, cptr, NULL);
      if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);
   }
   else
   if (NodeInstanceCount > 1)
   {
      cptr =
"<TR><TD COLSPAN=3 ALIGN=left>\n\
<TABLE CELLPADDING=3 CELLSPACING=0 BORDER=0>\n\
<TR><TH COLSPAN=2 ALIGN=left VALIGN=top>Instances</TH></TR>\n\
<TR><TD>&nbsp;</TD><TD ALIGN=left><FONT SIZE=-1>\n";
      status = NetWriteFaol (rqptr, cptr, NULL);
      if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);

      AdminMenuInstanceList (rqptr, NodeInstancePtr);

      cptr =
"</FONT></TD></TR>\n\
</TABLE>\n\
</TD></TR>\n";
      status = NetWriteFaol (rqptr, cptr, NULL);
      if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);
   }

   status = NetWriteFaol (rqptr, EndPageFao, NULL);
   if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);

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

   SysDclAst (NextTaskFunction, rqptr);
}

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

AdminVirtualServerMenu
(
REQUEST_STRUCT *rqptr,
REQUEST_AST NextTaskFunction
)
{
   char  BeginPageFao [] =
"<HTML>\n\
<HEAD>\n\
!AZ\
<TITLE>HTTPd !AZ ... Virtual Server Administration</TITLE>\n\
</HEAD>\n\
!AZ\n\
<H2><NOBR>HTTPd !AZ</NOBR></H2>\n\
<H3>Virtual Server Administration</H3>\n\
<P>\n\
\
<TABLE CELLPADDING=5 CELLSPACING=0 BORDER=1>\n\
<TR><TD>\n\
\
<TABLE CELLPADDING=3 CELLSPACING=0 BORDER=0>\n\
\
<TH ALIGN=left><U>Time</U>&nbsp;</TH>\
<TH ALIGN=right>Local:&nbsp;</TH>\
<TD><NOBR>!20&W</NOBR></TD></TR>\n\
\
<TH ALIGN=right COLSPAN=2>Universal:&nbsp;</TH>\
<TD><NOBR>!AZ (!AZ)</NOBR></TD></TR>\n\
\
<TH ALIGN=right COLSPAN=2><NOBR>System-Up:&nbsp;</NOBR></TH>\
<TD><NOBR>!%D</NOBR></TD></TR>\n\
\
<TR><TH></TH></TR>\n\
\
<TR>\n\
<TH ALIGN=right COLSPAN=2><U>Authentication</U>&nbsp;&nbsp;HTA:&nbsp;</TH>\n\
<TD>[<A HREF=\"!AZ\">Access</A>]</TD>\n\
</TR>\n\
<TR>\n\
<TH ALIGN=right COLSPAN=2>HTL:&nbsp;</TH>\n\
<TD>[<A HREF=\"!AZ\">Access</A>]</TD>\n\
</TR>\n\
<TR>\n\
<TH ALIGN=right COLSPAN=2>Cache:&nbsp;</TH>\n\
<TD>[<A HREF=\"!AZ\">Purge</A>]</TD>\n\
</TR>\n\
\
</TABLE>\n\
\
</TD></TR>\n\
</TABLE>\n\
\
</FORM>\n\
\
</BODY>\n\
</HTML>\n";

   static unsigned long  LibDeltaHours = LIB$K_DELTA_HOURS;

   static unsigned long  Pid = 0;
   static unsigned long  CurrentTime [2],
                         JpiLoginTime [2],
                         SystemUpTime [2];
   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 (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_ADMIN))
      WatchThis (rqptr, FI_LI, WATCH_MOD_ADMIN,
                 "AdminVirtualServerMenu() !&A", NextTaskFunction);

   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);
      SysDclAst (NextTaskFunction, rqptr);
      return;
   }

   sys$gettim (&CurrentTime);
   lib$sub_times (&CurrentTime, &SysInfo.BootBinTime, &SystemUpTime);

   rqptr->rqResponse.PreExpired = PRE_EXPIRE_ADMIN;
   RESPONSE_HEADER_200_HTML (rqptr);

   vecptr = FaoVector;

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

   *vecptr++ = Config.cfServer.AdminBodyTag;
   *vecptr++ = ServerHostPort;

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

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

   status = NetWriteFaol (rqptr, BeginPageFao, &FaoVector);
   if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);

   SysDclAst (NextTaskFunction, rqptr);
}

/*****************************************************************************/
/*
The 'InstanceList' coprises a string containing instance process names in the
format 'node::HTTPx:port' (e.g. DELTA::HTTPd: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 (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_ADMIN))
      WatchThis (rqptr, FI_LI, WATCH_MOD_ADMIN,
                 "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::HTTPx:port' (e.g. DELTA::HTTPd: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.
*/ 

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

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

   if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_ADMIN))
      WatchThis (rqptr, FI_LI, WATCH_MOD_ADMIN,
                 "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 (status, "InstanceSocketForAdmin()", FI_LI);
      if (IpPort)
      {
         *vecptr++ = "<A HREF=\"!AZ//!#AZ:!UL!AZ\">!AZ</A><BR>\n";
         *vecptr++ = rqptr->ServicePtr->RequestSchemeNamePtr;
         *vecptr++ = strchr(sptr,':') - sptr;
         *vecptr++ = sptr;
         *vecptr++ = IpPort;
         *vecptr++ = rqptr->rqHeader.PathInfoPtr;
         *vecptr++ = sptr;
      }
      else
      {
         *vecptr++ = "!AZ<BR>\n";
         *vecptr++ = sptr;
      }
      status = NetWriteFaol (rqptr, "!&@", &FaoVector);
      if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);
      if (*cptr = ch) cptr++;
   }
}

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

AdminReportServerStats
(
REQUEST_STRUCT *rqptr,
REQUEST_AST NextTaskFunction
)
{
/* 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 (~(0x00000000))


/*
   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 CELLPADDING=3 CELLSPACING=0 BORDER=1>\n\
<TR><TH>Environment</TH></TD></TR>\n\
<TR><TD>\n\
<TABLE CELLPADDING=0 CELLSPACING=5 BORDER=0>\n\
<TR><TH ALIGN=right>Version:</TH><TD>!AZ</TD></TR>\n\
<TR><TH ALIGN=right>Build:</TH><TD>!AZ</TD></TR>\n\
!&@\
<TR><TH ALIGN=right>System:</TH>\
<TD>!AZ with !UL CPU!%s and !ULMB running VMS !AZ</TD></TR>\n\
<TR><TH ALIGN=right>TCP/IP:</TH><TD>!AZ</TD></TR>\n\
<TR><TH ALIGN=right>Startup:</TH><TD>!&;AZ</TD></TR>\n\
</TABLE>\n\
</TD></TR>\n\
</TABLE>\n\
\
<P><TABLE CELLPADDING=3 CELLSPACING=0 BORDER=1>\n\
<TR><TH>Instances</TH></TR>\n\
<TR><TD>\n\
<TABLE CELLPADDING=0 CELLSPACING=5 BORDER=0>\n\
<TR><TH ALIGN=left><U>Node</U></TH><TH>&nbsp;</TH>\
<TH ALIGN=left><U>Cluster</U></TH></TR>\n\
<TR><TD VALIGN=top>!AZ</TD><TD>&nbsp;</TD><TD VALIGN=top>!AZ</TD></TR>\n\
</TABLE>\n\
</TD></TR>\n\
</TABLE>\n\
\
<P><TABLE CELLPADDING=3 CELLSPACING=0 BORDER=1>\n\
<TR><TH>!&?Server\rInstance\r Process</TH></TD></TR>\n\
<TR><TD>\n\
<TABLE CELLPADDING=0 CELLSPACING=5 BORDER=0>\n\
<TR>\
<TH ALIGN=right>Name:</TH><TD>!AZ</TD>\
<TH ALIGN=right>PID:</TH><TD><A HREF=\"!AZ?pid=!8XL!&@\">!8XL</A></TD>\
<TH ALIGN=right>User:</TH><TD>!AZ</TD>\
</TR>\n\
<TR>\
<TH ALIGN=right>AuthPriv:</TH><TD>!AZ</TD>\
<TH ALIGN=right>ImagPriv:</TH><TD>!AZ</TD>\
<TH ALIGN=right>CurPriv:</TH><TD>!AZ</TD>\
</TR>\n\
<TR>\
<TH ALIGN=right>Startup:</TH><TD>!UL</TD>\
<TH ALIGN=right>Last Exit:</TH><TD>!AZ</TD>\
<TH ALIGN=right>Zeroed:</TH><TD>!UL</TD>\
</TR>\n\
<TR>\
<TH ALIGN=right>Image:</TH><TD>!%D</TD>\
<TH ALIGN=right>Process:</TH><TD>!%D</TD>\
<TH ALIGN=right>CPU:</TH><TD>!UL !2ZL:!2ZL:!2ZL.!2ZL</TD>\n\
</TR>\n\
<TR>\
<TH ALIGN=right>Pg.Faults:</TH><TD>!UL</TD>\n\
<TH ALIGN=right>Pg.Used:</TH><TD>!UL%</TD>\
<TH ALIGN=right>Mode:</TH><TD>!AZ</TD>\
</TR>\n\
<TR>\
<TH ALIGN=right>WsSize:</TH><TD>!UL</TD>\
<TH ALIGN=right>WsPeak:</TH><TD>!UL</TD>\n\
<TH ALIGN=right>VirtPeak:</TH><TD>!UL</TD>\
</TR>\n\
<TR>\
<TH ALIGN=right>AST:</TH><TD>!UL&nbsp;/&nbsp;!UL</TD>\
<TH ALIGN=right>BIO:</TH><TD>!UL&nbsp;/&nbsp;!UL</TD>\
<TH ALIGN=right>BYT:</TH><TD>!UL&nbsp;/&nbsp;!UL</TD>\
</TR>\n\
<TR>\
<TH ALIGN=right>DIO:</TH><TD>!UL&nbsp;/&nbsp;!UL</TD>\
<TH ALIGN=right>ENQ:</TH><TD>!UL&nbsp;/&nbsp;!UL</TD>\
<TH ALIGN=right>FIL:</TH><TD>!UL&nbsp;/&nbsp;!UL</TD>\
</TR>\n\
<TR>\
<TH ALIGN=right>PRC:</TH><TD>!UL&nbsp;/&nbsp;!UL</TD>\
<TH ALIGN=right>TQ:</TH><TD>!UL&nbsp;/&nbsp;!UL</TD>\n\
</TR>\n\
<TR><TH ALIGN=right>Input:</TH>\
<TD COLSPAN=3><FONT SIZE=-1>!&@</FONT></TD></TR>\n\
<TR><TH ALIGN=right>Output:</TH>\
<TD COLSPAN=3><FONT SIZE=-1>!&@</FONT></TD></TR>\n\
</TABLE>\n\
</TD></TR>\n\
</TABLE>\n\
\
<P><TABLE CELLPADDING=3 CELLSPACING=0 BORDER=1>\n\
<TR><TH>System Resources</TH></TD></TR>\n\
<TR><TD>\n\
<TABLE CELLPADDING=0 CELLSPACING=5 BORDER=0>\n\
<TR><TD></TD><TH ALIGN=left><FONT SIZE=-1>GBLPAGES</FONT></TH>\
<TH ALIGN=left><FONT SIZE=-1>GBLSECTIONS</FONT></TH></TR>\n\
<TR><TH ALIGN=right>Temporary:</TH>\
<TD ALIGN=right>!UL</TD><TD ALIGN=right>!UL</TD></TR>\n\
<TR><TH ALIGN=right>Permanent:</TH>\
<TD ALIGN=right>!UL</TD><TD ALIGN=right>!UL</TD></TR>\n\
<TR><TH ALIGN=right>Total:</TH>\
<TD ALIGN=right>!UL</TD><TD ALIGN=right>!UL</TD></TR>\n\
</TABLE>\n\
</TD></TR>\n\
</TABLE>\n";

   static char  StatisticsFao [] =
"<P><TABLE CELLPADDING=3 CELLSPACING=0 BORDER=1>\n\
<TR><TH>Request Processing</TH></TR>\n\
<TR><TD>\n\
<TABLE CELLPADDING=0 CELLSPACING=0 BORDER=0>\n\
<TR><TD VALIGN=top>\n\
<TABLE CELLPADDING=0 CELLSPACING=5 BORDER=0>\n\
<TR><TH ALIGN=right><U>Connection</U></TH></TR>\n\
<TR><TH ALIGN=right>Total:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>Current:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>Peak:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>Keep-Alive&nbsp;&nbsp;/Current:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>/Peak:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>Busy:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>Accepted:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>Rejected:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>IPv4:</TH><TD>!UL&nbsp;&nbsp;(!UL%)&nbsp;&nbsp;</TD></TR>\n\
<TR><TH ALIGN=right>IPv6:</TH><TD>!UL&nbsp;&nbsp;(!UL%)&nbsp;&nbsp;</TD></TR>\n\
<TR><TH ALIGN=right>SSL:</TH><TD>!UL&nbsp;&nbsp;(!UL%)&nbsp;&nbsp;</TD></TR>\n\
<TR><TH ALIGN=right>Total&nbsp;&nbsp;/Rx:</TH><TD>!&,@SQ</TD></TR>\n\
<TR><TH ALIGN=right>/Tx:</TH><TD>!&,@SQ</TD></TR>\n\
<TR><TH ALIGN=right>Error&nbsp;&nbsp;/Rx:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>/Tx:</TH><TD>!UL</TD></TR>\n\
</TABLE>\n\
</TD><TD VALIGN=top>\n\
<TABLE CELLPADDING=0 CELLSPACING=5 BORDER=0>\n\
<TR><TH ALIGN=right><U>Request</U></TH></TR>\n\
<TR><TH ALIGN=right>Total:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>Current:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>Peak:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>&nbsp;Throttle&nbsp;&nbsp;/Queued:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>/Processing:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>/Busy:</TH><TD>!UL%</TD></TR>\n\
<TR><TH ALIGN=right>Redirect&nbsp;&nbsp;/Local:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>/Remote:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>&nbsp;Keep-Alive&nbsp;&nbsp;/Total:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>/Max:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>&nbsp;Pragma No-cache:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>Forbidden:</TH><TD>!UL</TD></TR>\n\
</TABLE>\n\
</TD><TD VALIGN=top>\n\
<TABLE CELLPADDING=0 CELLSPACING=5 BORDER=0>\n\
<TR><TH ALIGN=right><U>Method</U></TH></TR>\n\
<TR><TH ALIGN=right>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CONNECT:</TH>\
<TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>DELETE:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>GET:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>HEAD:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>POST:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>PUT:</TH><TD>!UL</TD></TR>\n\
<TR><TD>&nbsp;</TD></TR>\n\
<TR><TH ALIGN=right>&nbsp;<U>Duration</U></TH></TR>\n\
<TR><TH ALIGN=right>Min:</TH><TD ALIGN=right>!UL.!#ZL</TD></TR>\n\
<TR><TH ALIGN=right>Max:</TH><TD ALIGN=right>!UL.!#ZL</TD></TR>\n\
<TR><TH ALIGN=right>Ave:</TH><TD ALIGN=right>!UL.!#ZL</TD></TR>\n\
</TABLE>\n\
</TD><TD VALIGN=top>\n\
<TABLE CELLPADDING=0 CELLSPACING=5 BORDER=0>\n\
<TR><TH ALIGN=right>&nbsp;<U>Response</U></TH></TR>\n\
<TR><TH ALIGN=right><I>1nn</I>:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>2nn:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>3nn:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>4nn:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>5nn:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right><I>none</I>:</TH><TD>!UL</TD></TR>\n\
</TABLE>\n\
</TD></TR>\n\
</TABLE>\n\
</TD></TR>\n\
</TABLE>\n\
</TD></TR>\n\
</TABLE>\n\
\
<P><TABLE CELLPADDING=3 CELLSPACING=0 BORDER=1>\n\
<TR><TH>Processing Modules</TH></TR>\n\
<TR><TD>\n\
<TABLE CELLPADDING=0 CELLSPACING=0 BORDER=0>\n\
<TR><TD VALIGN=top>\n\
<TABLE CELLPADDING=0 CELLSPACING=5 BORDER=0>\n\
<TR><TH ALIGN=right>Admin:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>Directory:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>DotURL:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>File&nbsp;&nbsp;/All:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>/304:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>IsMap:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>Menu:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>Put:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>Proxy:</TH><TD COLSPAN=2>!UL</TD></TR>\n\
<TR><TH ALIGN=right>SSI:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>Update:</TH><TD>!UL</TD></TR>\n\
</TABLE>\n\
</TD><TD VALIGN=top>\n\
<TABLE CELLPADDING=0 CELLSPACING=5 BORDER=0>\n\
<TR><TH ALIGN=right><U>DCL</U></TH></TR>\n\
<TR><TH ALIGN=right>Default:</TH><TD>!AZ</TD></TR>\n\
<TR><TH ALIGN=right>CGI:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>CGIplus&nbsp;&nbsp;/All:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>/Reused:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>RTE&nbsp;&nbsp;/All:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>/Reused:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>Autoscripted:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>CLI:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>$CREPRC:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>$PERSONA!&?_MACRO\r\r&nbsp;&nbsp;/All:</TH>\
<TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>/Default:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>/Invalid:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>/Privileged:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>$FORCEX:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>$DELPRC:</TH><TD>!UL</TD></TR>\n\
</TABLE>\n\
</TD><TD VALIGN=top>\n\
<TABLE CELLPADDING=0 CELLSPACING=5 BORDER=0>\n\
<TR><TH ALIGN=right><U>DECnet</U></TH></TR>\n\
<TR><TH ALIGN=right>Default:</TH><TD>!AZ</TD></TR>\n\
<TR><TH ALIGN=right>Connect&nbsp;&nbsp;/All:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>/Reused:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>CGI:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>OSU:</TH><TD>!UL</TD></TR>\n\
</TABLE>\n\
</TD></TR>\n\
\
<TR><TD>\n\
<TABLE CELLPADDING=0 CELLSPACING=5 BORDER=0>\n\
</TABLE>\n\
</TD></TR>\n\
\
</TABLE>\n\
</TD></TR>\n\
</TABLE>\n\
\
<P><TABLE CELLPADDING=3 CELLSPACING=0 BORDER=1>\n\
<TR><TH>!&?\rInstance \rFile Cache</TH></TR>\n\
<TR><TD>\n\
<TABLE CELLPADDING=0 CELLSPACING=5 BORDER=0>\n\
<TR><TH ALIGN=right>Entries&nbsp;&nbsp;/Permanent:</TH>\
<TD COLSPAN=2>!UL</TD></TR>\n\
<TR><TH ALIGN=right>/Volatile:</TH><TD COLSPAN=2>!UL</TD></TR>\n\
<TR><TH ALIGN=right>/Max:</TH><TD COLSPAN=2>!UL</TD></TR>\n\
<TR><TH ALIGN=right>Memory&nbsp;&nbsp;/Permanent:</TH>\
<TD COLSPAN=2>!UL&nbsp;kB</TD></TR>\n\
<TR><TH ALIGN=right>/Volatile:</TH><TD COLSPAN=2>!UL&nbsp;kB</TD></TR>\n\
<TR><TH ALIGN=right>/Max:</TH><TD COLSPAN=2>!UL&nbsp;kB</TD></TR>\n\
<TR><TH ALIGN=right>Loads:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>Not Hit:</TH><TD>!UL</TD><TD>!UL%</TD></TR>\n\
<TR><TH ALIGN=right>Total Hit:</TH><TD>!UL</TD><TD>!UL%</TD></TR>\n\
<TR><TH ALIGN=right>1-9:</TH><TD>!UL</TD><TD>!UL%</TD></TR>\n\
<TR><TH ALIGN=right>10-99:</TH><TD>!UL</TD><TD>!UL%</TD></TR>\n\
<TR><TH ALIGN=right>100-999:</TH><TD>!UL</TD><TD>!UL%</TD></TR>\n\
<TR><TH ALIGN=right>&gt;1000:</TH><TD>!UL</TD><TD>!UL%</TD></TR>\n\
<TR><TD COLSPAN=3><FONT SIZE=-1>\
<SUP>*</SUP><I>counts are per-startup only</I></FONT></TD></TR>\n\
</TABLE>\n\
</TD></TR>\n\
</TABLE>\n\
\
<P><TABLE CELLPADDING=3 CELLSPACING=0 BORDER=1>\n\
<TR><TH>Authorization</TH></TR>\n\
<TR><TD>\n\
<TABLE CELLPADDING=0 CELLSPACING=0 BORDER=0>\n\
<TR><TH><U>Paths</U></TH><TH COLSPAN=2><U>Authentication</U></TH></TR>\n\
<TR><TD VALIGN=top>\n\
<TABLE CELLPADDING=0 CELLSPACING=5 BORDER=0>\n\
<TR><TH ALIGN=right COLSPAN=2>Success:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right COLSPAN=2>Failure:</TH><TD>!UL</TD></TR>\n\
</TABLE>\n\
</TD><TD VALIGN=top>\n\
<TABLE CELLPADDING=0 CELLSPACING=5 BORDER=0>\n\
<TR><TH ALIGN=right><U>Source</U></TH>\n\
<TR><TH ALIGN=right>Cache:</TH><TD>!UL</TD>\n\
<TR><TH ALIGN=right>ACME:</TH><TD >!UL</TD>\n\
<TR><TH ALIGN=right>Agent:</TH><TD >!UL</TD>\n\
<TR><TH ALIGN=right>HTA:</TH><TD >!UL</TD>\n\
<TR><TH ALIGN=right>List:</TH><TD >!UL</TD>\n\
<TR><TH ALIGN=right>RFC1413:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>Skeleton-Key:</TH><TD>!UL&nbsp;!AZ</TD></TR>\n\
<TR><TH ALIGN=right>SYSUAF:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>X509:</TH><TD>!UL</TD></TR>\n\
</TABLE>\n\
</TD><TD VALIGN=top>\n\
<TABLE CELLPADDING=0 CELLSPACING=5 BORDER=0>\n\
<TR><TH ALIGN=right><U>Scheme</U></TH></TR>\n\
<TR><TH ALIGN=right>Basic:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>Digest:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>Other:</TH><TD>!UL</TD></TR>\n\
</TABLE>\n\
</TD></TR>\n\
\
<TR><TD COLSPAN=2><TABLE CELLPADDING=0 CELLSPACING=5 BORDER=0>\n\
</TABLE></TD></TR>\n\
\
</TABLE>\n\
</TD></TR>\n\
</TABLE>\n";

   static char LookupFao [] =
"<P><TABLE CELLPADDING=3 CELLSPACING=0 BORDER=1>\n\
<TR><TH>Host Resolution</TH></TR>\n\
<TR><TD>\n\
<TABLE CELLPADDING=0 CELLSPACING=5 BORDER=0>\n\
<TR><TH></TH>\
<TH ALIGN=right>&nbsp;&nbsp;<U>Literal</U></TH>\
<TH ALIGN=right COLSPAN=2><CENTER><U>DNS</U></CENTER></TH>\
<TH ALIGN=right>&nbsp;&nbsp;<U>Cache</U></TH>\
<TH ALIGN=right>&nbsp;&nbsp;<U>Error</U>&nbsp</TH>\
</TR>\n\
<TR><TH ALIGN=right>Address:</TH>\
<TD ALIGN=right>&nbsp;&nbsp;!UL</TD>\
<TD></TD>\
<TD></TD>\
<TD></TD>\
<TD ALIGN=right>&nbsp;&nbsp;!UL&nbsp;</TD>\
</TR>\n\
<TR><TH ALIGN=right>Address-to-name:</TH>\
<TD></TD>\
<TD ALIGN=right>&nbsp;&nbsp;!UL</TD>\
<TD ALIGN=left>&nbsp;(!UL%)</TD>\
<TD ALIGN=right>&nbsp;&nbsp;!UL</TD>\
<TD ALIGN=right>&nbsp;&nbsp;!UL&nbsp;</TD>\
</TR>\n\
<TR><TH ALIGN=right>Name-to-address:</TH>\
<TD></TD>\
<TD ALIGN=right>&nbsp;&nbsp;!UL</TD>\
<TD ALIGN=left>&nbsp;(!UL%)</TD>\
<TD ALIGN=right>&nbsp;&nbsp;!UL</TD>\
<TD ALIGN=right>&nbsp;&nbsp;!UL&nbsp;</TD>\
</TR>\n\
</TABLE>\n\
</TD></TR>\n\
</TABLE>\n\
\
<P><TABLE CELLPADDING=3 CELLSPACING=0 BORDER=1>\n\
<TR><TH>Other</TH></TR>\n\
<TR><TD>\n\
<TABLE CELLPADDING=0 CELLSPACING=5 BORDER=0>\n\
<TR><TH ALIGN=right>Path Alert:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>NCS Conversion:</TH><TD>!UL&nbsp;/&nbsp;!UL</TD></TR>\n\
<TR><TH ALIGN=right>StreamLF File Conversion:</TH><TD>!UL</TD></TR>\n\
<TR><TH ALIGN=right>Errors Noticed:</TH><TD>!UL!&@</TD></TR>\n\
<TR><TH ALIGN=right>$HIBER spurious wake:</TH><TD>!UL</TD></TR>\n\
</TABLE>\n\
</TD></TR>\n\
</TABLE>\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],
                LastExitStatus [16];

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

   static $DESCRIPTOR (LastExitStatusDsc, LastExitStatus);
   static $DESCRIPTOR (LastExitStatusFaoDsc, "%X!XL\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  status,
        ConnectTotalCount,
        Ipv4Percent,
        Ipv6Percent,
        MatchPercent,
        PercentDnsAddress,
        PercentDnsName,
        StringTotal,
        SslPercent,
        TotalCount;
   unsigned int  bit;
   unsigned long  ResponseDuration,
                  Remainder,
                  Seconds,
                  SubSeconds;
   unsigned long  *vecptr;
   unsigned long  FaoVector [128],
                  QuadBytesRawRx [2],
                  QuadBytesRawTx [2];
   char  *cptr,
         *AuthPrivPtr,
         *CurPrivPtr,
         *ImagPrivPtr,
         *ClusterInstancePtr,
         *NodeInstancePtr,
         *SesolaVersionPtr;
   IO_SB  IOsb;

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

   if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_ADMIN))
      WatchThis (rqptr, FI_LI, WATCH_MOD_ADMIN,
                 "AdminReportServerStats() !&A", NextTaskFunction);

   SesolaVersionPtr = SesolaVersion ();

   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);
      SysDclAst (NextTaskFunction, 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);
      SysDclAst (NextTaskFunction, rqptr);
      return;
   }

   InstanceLockList (INSTANCE_CLUSTER, "<BR>", &ClusterInstancePtr);
   InstanceLockList (INSTANCE_NODE, "<BR>", &NodeInstancePtr);

   JpiUserName[12] = '\0';
   for (cptr = JpiUserName; *cptr && *cptr != ' '; cptr++);
   *cptr = '\0';
   if (Debug) fprintf (stdout, "JpiUserName |%s|\n", JpiUserName);

   JpiPrcNam[15] = '\0';
   for (cptr = JpiPrcNam; *cptr && *cptr != ' '; cptr++);
   *cptr = '\0';
   if (Debug) fprintf (stdout, "JpiPrcNam |%s|\n", JpiPrcNam);

   sys$gettim (&CurrentTime);
   lib$sub_times (&CurrentTime, &HttpdStartBinTime, &ImageUpTime);
   lib$sub_times (&CurrentTime, &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;

   rqptr->rqResponse.PreExpired = PRE_EXPIRE_ADMIN;
   RESPONSE_HEADER_200_HTML (rqptr);
   AdminPageTitle (rqptr, "Server Statistics");

   InstanceMutexLock (INSTANCE_MUTEX_HTTPD);

   ConnectTotalCount = AccountingPtr->ConnectIpv4Count +
                       AccountingPtr->ConnectIpv6Count;

   if (AccountingPtr->ConnectSslCount)
      SslPercent = (AccountingPtr->ConnectSslCount * 100) / ConnectTotalCount;
   else
      SslPercent = 0;

   if (AccountingPtr->ConnectIpv4Count)
      Ipv4Percent = (AccountingPtr->ConnectIpv4Count * 100) / ConnectTotalCount;
   else
      Ipv4Percent = 0;

   if (AccountingPtr->ConnectIpv6Count)
      Ipv6Percent = (AccountingPtr->ConnectIpv6Count * 100) / ConnectTotalCount;
   else
      Ipv6Percent = 0;

   if (AccountingPtr->LastExitStatus)
      sys$fao (&LastExitStatusFaoDsc, 0, &LastExitStatusDsc, 
               AccountingPtr->LastExitStatus);
   else
      strcpy (LastExitStatus, "<I>(none)</I>");

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

   vecptr = FaoVector;

   *vecptr++ = SoftwareID;
   *vecptr++ = BuildInfo;
   if (SesolaVersionPtr[0])
   {
      *vecptr++ = "<TR><TH ALIGN=right>SSL:</TH><TD>!AZ</TD></TR>\n";
      *vecptr++ = SesolaVersionPtr;
   }
   else
      *vecptr++ = "";
   *vecptr++ = SysInfo.HwName;
   *vecptr++ = SysInfo.AvailCpuCnt;
   *vecptr++ = SysInfo.MemoryMB;
   *vecptr++ = SysInfo.Version;
   *vecptr++ = TcpIpAgentInfo;
   *vecptr++ = CommandLine;

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

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

   /***********/
   /* 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++ = AccountingPtr->StartupCount;
   *vecptr++ = LastExitStatus;
   *vecptr++ = AccountingPtr->ZeroedCount;
   *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++ = 100 - ((JpiPagFilCnt * 100) / HttpdProcess.PgFlQuo);
   *vecptr++ = HttpdProcess.ModeName;
   *vecptr++ = JpiWsSize;
   *vecptr++ = JpiWsPeak;
   *vecptr++ = JpiVirtPeak;
   *vecptr++ = JpiAstCnt;
   *vecptr++ = HttpdProcess.AstLm;
   *vecptr++ = JpiBioCnt;
   *vecptr++ = HttpdProcess.BioLm;
   *vecptr++ = JpiBytCnt;
   *vecptr++ = HttpdProcess.BytLm;
   *vecptr++ = JpiDioCnt;
   *vecptr++ = HttpdProcess.DioLm;
   *vecptr++ = JpiEnqCnt;
   *vecptr++ = HttpdProcess.EnqLm;
   *vecptr++ = JpiFilCnt;
   *vecptr++ = HttpdProcess.FilLm;
   *vecptr++ = JpiPrcCnt;
   *vecptr++ = HttpdProcess.PrcLm;
   *vecptr++ = JpiTqCnt;
   *vecptr++ = 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;

   status = NetWriteFaol (rqptr, BeginPageFao, &FaoVector);
   if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);

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

   NetServiceReportStats (rqptr);

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

   InstanceMutexLock (INSTANCE_MUTEX_HTTPD);

   /* get these values because of the locking requirements */
   memcpy (&QuadBytesRawRx, &AccountingPtr->QuadBytesRawRx, 8);
   memcpy (&QuadBytesRawTx, &AccountingPtr->QuadBytesRawTx, 8);

   vecptr = FaoVector;

   /* connections */
   *vecptr++ = ConnectTotalCount;
   *vecptr++ = AccountingPtr->ConnectCurrent;
   *vecptr++ = AccountingPtr->ConnectPeak;
   *vecptr++ = AccountingPtr->ConnectCurrentKeepAlive;
   *vecptr++ = AccountingPtr->ConnectPeakKeepAlive;
   *vecptr++ = AccountingPtr->ConnectTooBusyCount;
   *vecptr++ = AccountingPtr->ConnectAcceptedCount;
   *vecptr++ = AccountingPtr->ConnectRejectedCount;
   *vecptr++ = AccountingPtr->ConnectIpv4Count;
   *vecptr++ = Ipv4Percent;
   *vecptr++ = AccountingPtr->ConnectIpv6Count;
   *vecptr++ = Ipv6Percent;
   *vecptr++ = AccountingPtr->ConnectSslCount;
   *vecptr++ = SslPercent;
   *vecptr++ = &QuadBytesRawRx;
   *vecptr++ = &QuadBytesRawTx;
   *vecptr++ = AccountingPtr->NetReadErrorCount;
   *vecptr++ = AccountingPtr->NetWriteErrorCount;

   /* requests */
   *vecptr++ = AccountingPtr->RequestTotalCount;
   *vecptr++ = AccountingPtr->ConnectProcessing;
   *vecptr++ = AccountingPtr->ConnectProcessingPeak;
   *vecptr++ = AccountingPtr->ThrottleCurrentlyQueued;
   *vecptr++ = AccountingPtr->ThrottleCurrentlyProcessing;
   *vecptr++ = AccountingPtr->ThrottleBusyMetric;
   *vecptr++ = AccountingPtr->RedirectLocalCount;
   *vecptr++ = AccountingPtr->RedirectRemoteCount;
   *vecptr++ = AccountingPtr->RequestKeepAliveCount;
   *vecptr++ = AccountingPtr->RequestKeepAliveMax;
   *vecptr++ = AccountingPtr->RequestPragmaNoCacheCount;
   *vecptr++ = AccountingPtr->RequestForbiddenCount;

   /* HTTP methods */
   *vecptr++ = AccountingPtr->MethodConnectCount;
   *vecptr++ = AccountingPtr->MethodDeleteCount;
   *vecptr++ = AccountingPtr->MethodGetCount;
   *vecptr++ = AccountingPtr->MethodHeadCount;
   *vecptr++ = AccountingPtr->MethodPostCount;
   *vecptr++ = AccountingPtr->MethodPutCount;

   /* response duration */
   if (AccountingPtr->ResponseDurationMin == -1)
   {
      *vecptr++ = 0;
      *vecptr++ = DURATION_DECIMAL_PLACES;
      *vecptr++ = 0;
   }
   else
   {
      Seconds = AccountingPtr->ResponseDurationMin / USECS_IN_A_SECOND;
      SubSeconds = (AccountingPtr->ResponseDurationMin % USECS_IN_A_SECOND) /
                   DURATION_UNITS_USECS;
      *vecptr++ = Seconds;
      *vecptr++ = DURATION_DECIMAL_PLACES;
      *vecptr++ = SubSeconds;
   }
   Seconds = AccountingPtr->ResponseDurationMax / USECS_IN_A_SECOND;
   SubSeconds = (AccountingPtr->ResponseDurationMax % USECS_IN_A_SECOND) /
                DURATION_UNITS_USECS;
   *vecptr++ = Seconds;
   *vecptr++ = DURATION_DECIMAL_PLACES;
   *vecptr++ = SubSeconds;

   if (AccountingPtr->ResponseDurationCount)
   {
      status = lib$ediv (&AccountingPtr->ResponseDurationCount,
                         &AccountingPtr->QuadResponseDuration,
                         &ResponseDuration, &Remainder);
      if (Debug) fprintf (stdout, "lib$ediv() %%X%08.08X\n", status);
   }
   else
      ResponseDuration = 0;

   Seconds = ResponseDuration / USECS_IN_A_SECOND;
   SubSeconds = (ResponseDuration % USECS_IN_A_SECOND) / DURATION_UNITS_USECS;
   *vecptr++ = Seconds;
   *vecptr++ = DURATION_DECIMAL_PLACES;
   *vecptr++ = SubSeconds;

   /* responses */
   *vecptr++ = AccountingPtr->ResponseStatusCodeCountArray[1];
   *vecptr++ = AccountingPtr->ResponseStatusCodeCountArray[2];
   *vecptr++ = AccountingPtr->ResponseStatusCodeCountArray[3];
   *vecptr++ = AccountingPtr->ResponseStatusCodeCountArray[4];
   *vecptr++ = AccountingPtr->ResponseStatusCodeCountArray[5];
   *vecptr++ = AccountingPtr->ResponseStatusCodeCountArray[0];

   /* modules */
   *vecptr++ = AccountingPtr->DoServerAdminCount;
   *vecptr++ = AccountingPtr->DoDirectoryCount;
   *vecptr++ = AccountingPtr->DoDotUrlCount;
   *vecptr++ = AccountingPtr->DoFileCount;
   *vecptr++ = AccountingPtr->DoFileNotModifiedCount;
   *vecptr++ = AccountingPtr->DoIsMapCount;
   *vecptr++ = AccountingPtr->DoMenuCount;
   *vecptr++ = AccountingPtr->DoPutCount;
   *vecptr++ = ProxyAccountingPtr->MethodConnectCount +
               ProxyAccountingPtr->MethodGetCount +
               ProxyAccountingPtr->MethodHeadCount +
               ProxyAccountingPtr->MethodPostCount +
               ProxyAccountingPtr->MethodPutCount,
   *vecptr++ = AccountingPtr->DoSsiCount;
   *vecptr++ = AccountingPtr->DoUpdateCount;
   if (HttpdScriptAsUserName[0])
      *vecptr++ = HttpdScriptAsUserName;
   else
      *vecptr++ = HttpdProcess.UserName;
   *vecptr++ = AccountingPtr->DoScriptCount;
   *vecptr++ = AccountingPtr->DoCgiPlusScriptCount;
   *vecptr++ = AccountingPtr->DclCgiPlusReusedCount;
   *vecptr++ = AccountingPtr->DoRteScriptCount;
   *vecptr++ = AccountingPtr->DclRteReusedCount;
   *vecptr++ = AccountingPtr->DoAutoScriptCount;
   *vecptr++ = AccountingPtr->DoDclCommandCount;
   *vecptr++ = AccountingPtr->DclCrePrcCount;
   *vecptr++ = PersonaMacro;
   *vecptr++ = AccountingPtr->DclCrePrcPersonaCount;
   *vecptr++ = AccountingPtr->DclCrePrcPersonaDefaultCount;
   *vecptr++ = AccountingPtr->DclCrePrcPersonaInvUserCount;
   *vecptr++ = AccountingPtr->DclCrePrcPersonaPrvUserCount;
   *vecptr++ = AccountingPtr->DclForceXCount;
   *vecptr++ = AccountingPtr->DclDelPrcCount;
   if (HttpdScriptAsUserName[0])
      *vecptr++ = HttpdScriptAsUserName;
   else
      *vecptr++ = HttpdProcess.UserName;
   *vecptr++ = AccountingPtr->DoDECnetCount;
   *vecptr++ = AccountingPtr->DoDECnetCount -
               AccountingPtr->DoDECnetCgiCount -
               AccountingPtr->DoDECnetOsuCount;
   *vecptr++ = AccountingPtr->DoDECnetCgiCount;
   *vecptr++ = AccountingPtr->DoDECnetOsuCount;

   /* cache */
   *vecptr++ = (InstanceNodeCurrent <= 1);
   *vecptr++ = CachePermEntryCount;
   *vecptr++ = CacheEntryCount;
   *vecptr++ = CacheEntriesMax;
   *vecptr++ = CachePermMemoryInUse >> 10;
   *vecptr++ = CacheMemoryInUse >> 10;
   *vecptr++ = CacheTotalKBytesMax;
   *vecptr++ = CacheLoadCount;
   *vecptr++ = CacheHits0;
   if (CacheLoadCount)
      *vecptr++ = CacheHits0 * 100 / CacheLoadCount;
   else
      *vecptr++ = 0;
   *vecptr++ = CacheHitCount;
   if (CacheLoadCount)
      *vecptr++ = CacheHitCount * 100 / CacheLoadCount;
   else
      *vecptr++ = 0;
   *vecptr++ = CacheHits10;
   if (CacheHitCount)
      *vecptr++ = CacheHits10 * 100 / CacheHitCount;
   else
      *vecptr++ = 0;
   *vecptr++ = CacheHits100;
   if (CacheHitCount)
      *vecptr++ = CacheHits100 * 100 / CacheHitCount;
   else
      *vecptr++ = 0;
   *vecptr++ = CacheHits1000;
   if (CacheHitCount)
      *vecptr++ = CacheHits1000 * 100 / CacheHitCount;
   else
      *vecptr++ = 0;
   *vecptr++ = CacheHits1000plus;
   if (CacheHitCount)
      *vecptr++ = CacheHits1000plus * 100 / CacheHitCount;
   else
      *vecptr++ = 0;

   /* authorization */
   /*
      Note that the total number of paths being authorized may be
      greater than the total number of authentications.  This happens
      if a request contains both script and path authorizations,
      resulting in two authorizations for the one request!
   */
   *vecptr++ = AccountingPtr->AuthAuthorizedCount;
   *vecptr++ = AccountingPtr->AuthNotAuthorizedCount;
   *vecptr++ = AccountingPtr->AuthBasicCount +
               AccountingPtr->AuthDigestCount +
               AccountingPtr->AuthOtherCount -
               AccountingPtr->AuthAcmeCount -
               AccountingPtr->AuthAgentCount -
               AccountingPtr->AuthHtDatabaseCount -
               AccountingPtr->AuthRFC1413Count -
               AccountingPtr->AuthSimpleListCount -
               AccountingPtr->AuthVmsCount -
               AccountingPtr->AuthX509Count;
   *vecptr++ = AccountingPtr->AuthAcmeCount;
   *vecptr++ = AccountingPtr->AuthAgentCount;
   *vecptr++ = AccountingPtr->AuthHtDatabaseCount;
   *vecptr++ = AccountingPtr->AuthSimpleListCount;
   *vecptr++ = AccountingPtr->AuthRFC1413Count;
   *vecptr++ = AccountingPtr->AuthSkelKeyCount;
   if (HttpdGblSecPtr->AuthSkelKeyHttpdTickSecond)
      *vecptr++ = "(<B>Active</B>)";
   else
      *vecptr++ = "(Inactive)";
   *vecptr++ = AccountingPtr->AuthVmsCount;
   *vecptr++ = AccountingPtr->AuthX509Count;
   *vecptr++ = AccountingPtr->AuthBasicCount;
   *vecptr++ = AccountingPtr->AuthDigestCount;
   *vecptr++ = AccountingPtr->AuthOtherCount;

   InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);

   status = NetWriteFaol (rqptr, StatisticsFao, &FaoVector);
   if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);

   /* proxy serving */
   ProxyMaintStatisticsReport (rqptr, "Proxy");

   InstanceMutexLock (INSTANCE_MUTEX_HTTPD);

   /* lookup and other */
   vecptr = FaoVector;

   TotalCount = AccountingPtr->LookupDnsAddressCount +
                AccountingPtr->LookupCacheAddressCount;
   if (TotalCount)
      PercentDnsAddress = AccountingPtr->LookupDnsAddressCount * 100 /
                          TotalCount;
   else
      PercentDnsAddress = 0;

   TotalCount = AccountingPtr->LookupDnsNameCount +
                AccountingPtr->LookupCacheNameCount;
   if (TotalCount)
      PercentDnsName = AccountingPtr->LookupDnsNameCount * 100 / TotalCount;
   else
      PercentDnsName = 0;

   *vecptr++ = AccountingPtr->LookupLiteralCount;
   *vecptr++ = AccountingPtr->LookupLiteralErrorCount;
   *vecptr++ = AccountingPtr->LookupDnsAddressCount;
   *vecptr++ = PercentDnsAddress;
   *vecptr++ = AccountingPtr->LookupCacheAddressCount;
   *vecptr++ = AccountingPtr->LookupDnsAddressErrorCount;
   *vecptr++ = AccountingPtr->LookupDnsNameCount;
   *vecptr++ = PercentDnsName;
   *vecptr++ = AccountingPtr->LookupCacheNameCount;
   *vecptr++ = AccountingPtr->LookupDnsNameErrorCount;

   *vecptr++ = AccountingPtr->PathAlertCount;
   *vecptr++ = AccountingPtr->NcsCount;
   *vecptr++ = AccountingPtr->NcsConvertCount;
   *vecptr++ = AccountingPtr->StreamLfConversionCount;
   *vecptr++ = AccountingPtr->ErrorsNoticedCount;

   if (AccountingPtr->ErrorsNoticedCount &&
       HttpdProcess.SysOutputFile)
   {
      *vecptr++ = "&nbsp;&nbsp;(see&nbsp;<A HREF=\"!AZ\">Log</A>)";
      *vecptr++ = ADMIN_REPORT_PROCESS_OUTPUT;
   }
   else
      *vecptr++ = "";

   *vecptr++ = AccountingPtr->SpuriousWakeCount;

   InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);

   status = NetWriteFaol (rqptr, LookupFao, &FaoVector);
   if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);

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

   SysDclAst (NextTaskFunction, rqptr);
}

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

AdminServerProcessInput
(
REQUEST_STRUCT *rqptr,
REQUEST_AST NextTaskFunction
)
{
   static char  BeginPage [] =
"<P><TABLE CELLSPACING=0 CELLPADDING=5 BORDER=1>\n\
<TR><TH>!AZ</TH></TR>\n\
<TR><TD>\n\
<TABLE CELLSPACING=0 CELLPADDING=5 BORDER=0>\n\
<TR><TD><PRE>";

   int  status;

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

   if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_ADMIN))
      WatchThis (rqptr, FI_LI, WATCH_MOD_ADMIN, "AdminServerProcessInput()");

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

   rqptr->rqResponse.PreExpired = PRE_EXPIRE_ADMIN;
   RESPONSE_HEADER_200_PLAIN (rqptr);

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

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

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

AdminServerProcessOutput
(
REQUEST_STRUCT *rqptr,
REQUEST_AST NextTaskFunction
)
{
   static char  BeginPage [] =
"<P><TABLE CELLSPACING=0 CELLPADDING=5 BORDER=1>\n\
<TR><TH>!AZ</TH></TR>\n\
<TR><TD>\n\
<TABLE CELLSPACING=0 CELLPADDING=5 BORDER=0>\n\
<TR><TD><PRE>";

   int  status;

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

   if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_ADMIN))
      WatchThis (rqptr, FI_LI, WATCH_MOD_ADMIN, "AdminServerProcessOutput()");

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

   rqptr->rqResponse.PreExpired = PRE_EXPIRE_ADMIN;
   RESPONSE_HEADER_200_PLAIN (rqptr);

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

   FileBegin (rqptr, NextTaskFunction, 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 [] =
"<HTML>\n\
<HEAD>\n\
!AZ\
<TITLE>HTTPd !AZ ... !AZ</TITLE>\n\
</HEAD>\n\
!AZ\n\
<H2><NOBR>HTTPd !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 (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_ADMIN))
      WatchThis (rqptr, FI_LI, WATCH_MOD_ADMIN,
                 "AdminPageTitle() !UL !&Z", argcnt, Title);

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

   RESPONSE_HEADER_200_HTML (rqptr);

   vecptr = FaoVector;

   *vecptr++ = HtmlMetaInfo (rqptr, NULL);
   *vecptr++ = ServerHostPort;
   *vecptr++ = Title;
   *vecptr++ = Config.cfServer.AdminBodyTag;
   *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.Vms64bit;

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

   status = NetWriteFaol (rqptr, PageTitleFao, &FaoVector);
   if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);

   return (status);
}

/*****************************************************************************/
/*
Provide a list of problems reported during a meta-config load in standard
admininistration menu/report format.
*/
 
AdminMetaConReport
(
REQUEST_STRUCT *rqptr,
META_CONFIG *mcptr,
META_CONFIG *MetaConStartupPtr
)
{
   static char  ReportFao [] =
"<P><TABLE CELLPADDING=3 CELLSPACING=0 BORDER=1>\n\
<TR><TH>&nbsp;&nbsp;!UL Informational,&nbsp; !UL Warning,&nbsp; \
!&?<FONT COLOR=\"#ff0000\">\r\r!-!UL Error!%s&nbsp;!-!&?</FONT>\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 (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_ADMIN))
      WatchThis (rqptr, FI_LI, WATCH_MOD_ADMIN, "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;

   status = NetWriteFaol (rqptr, ReportFao, &FaoVector);
   if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);
}

/*****************************************************************************/
/*
Provide a description of the source of this configuration in an administration
standard menu/report format.
*/
 
AdminMetaConSource
(
REQUEST_STRUCT *rqptr,
META_CONFIG *mcptr,
META_CONFIG *MetaConStartupPtr,
BOOL IncludeFileDetected
)
{
   static char  SourceFao [] =
"<P><TABLE CELLPADDING=3 CELLSPACING=0 BORDER=1>\n\
<TR><TH>Source: &quot;!&?Server\rFile\r&quot;</TH></TR>\n\
<TR><TD>\n\
<TABLE CELLPADDING=3 CELLSPACING=0 BORDER=0>\n\
<TR>\
<TH ALIGN=right>File:</TH>\
<TD ALIGN=left>!AZ</TD>\
<TD>&nbsp;</TD><TH>[<A HREF=\"!AZ\">View</A>]</TH>\
<TH>[<A HREF=\"!AZ?do=edit\">Edit</A>]</TH>\
</TR>\n\
<TR><TH ALIGN=right>!AZ</TH>\
<TD ALIGN=left>!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 (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_ADMIN))
      WatchThis (rqptr, FI_LI, WATCH_MOD_ADMIN, "AdminMetaConSource()");

   vecptr = FaoVector;
   *vecptr++ = (mcptr == MetaConStartupPtr);
   *vecptr++ = mcptr->LoadReport.FileName;
   *vecptr++ = cptr = rqptr->rqHeader.PathInfoPtr;
   *vecptr++ = cptr;
   if (mcptr == MetaConStartupPtr)
   {
      *vecptr++ = "Loaded:";
      *vecptr++ = &mcptr->LoadReport.LoadBinTime;
   }
   else
   {
      *vecptr++ = "Revised:";
      *vecptr++ = &mcptr->LoadReport.FileBinTime;
   }

   if (IncludeFileDetected)
      *vecptr++ =
"<TR><TD COLSPAN=2><FONT COLOR=\"#ff0000\"><CENTER>\
<B>Source contained [IncludeFile]</B><BR>\
CAUTION: Using this interface will overwrite that!!\
</CENTER></FONT></TD></TR>\n";
   else
      *vecptr++ = "";

   status = NetWriteFaol (rqptr, SourceFao, &FaoVector);
   if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);
}

/*****************************************************************************/
/*
For administration menu configuration update pages provide the <FORM..> element
and some leading, commented-out descriptive text for the configuration file.
*/
 
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 (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_ADMIN))
      WatchThis (rqptr, FI_LI, WATCH_MOD_ADMIN,
                 "AdminMetaConBeginUpdateForm()");

   vecptr = FaoVector;

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

   status = NetWriteFaol (rqptr, BeginFormFao, &FaoVector);
   if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);
}

/*****************************************************************************/
/*
For administration menu configuration update pages provide the update buttons
and the </FORM> tag (complements AdminMetaConEndUpdateForm()).
*/
 
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 (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_ADMIN))
      WatchThis (rqptr, FI_LI, WATCH_MOD_ADMIN,
                 "AdminMetaConEndUpdateForm()");

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

/*****************************************************************************/
/*
For administration menu configuration update pages provide the update buttons
and the </FORM> tag (complements AdminMetaConEndUpdateForm()).
*/
 
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 (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_ADMIN))
      WatchThis (rqptr, FI_LI, WATCH_MOD_ADMIN,
                 "AdminVirtualServiceForm()");

   vecptr = FaoVector;
   *vecptr++ = FormAction;
   *vecptr++ = UseServerDatabase;
   *vecptr++ = VirtualService[0];
   *vecptr++ = (ServiceCount == 1);
   status = NetWriteFaol (rqptr, BeginFormFao, &FaoVector);
   if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);

   if (ServiceCount > 1)
   {
      for (svptr = ServiceListHead; svptr; svptr = svptr->NextPtr)
      {
         vecptr = FaoVector;
         *vecptr++ = svptr->ServerHostPort;
         *vecptr++ = strsame (svptr->ServerHostPort, VirtualService, -1);
         *vecptr++ = svptr->ServerHostPort;

         status = NetWriteFaol (rqptr,
                     "<OPTION VALUE=\"!AZ\"!&? SELECTED\r\r>!AZ\n",
                     &FaoVector);
         if (VMSnok (status)) ErrorNoticed (status, "NetWriteFaol()", FI_LI);
      }
   }

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

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

