/*****************************************************************************/
/*
                               ProxyMaint.c

See PROXYCACHE.C for commentary.  This module implements both the ROUTINE and
REACTIVE purge maintenance for the proxy file cache.  See PROXYCACHE.C module
for information on cache management strategy.

Routine proxy maintenance can be initiated from the command line.

  /PROXY=PURGE=BACKGROUND[=ProxyCachePurgeList]
  /PROXY=PURGE=REACTIVE[=ProxyCachePurgeList]
  /PROXY=PURGE=ROUTINE[=ProxyCachePurgeList]
  /PROXY=CACHE=STATISTICS


VERSION HISTORY
---------------
30-APR-2004  MGD  use QIO to erase cache file (save a few cycles)
13-JAN-2004  MGD  DECC 6.2 objected to '$DESCRIPTOR(name,ptr->string)'
11-JUN-2003  MGD  bugfix; ProxyMaintDeviceStats() volume count (set) handling
11-MAY-2003  MGD  proxy unknown request fields
29-APR-2003  MGD  add proxy cache device error count statistics
02-APR-2003  MGD  maintain 'ProxyXForwardedFor',
                  modify for 'ProxyForwarded'
20-FEB-2003  MGD  ProxytMaintSupervisor() used instead of timers,
                  background purge (set with '[ProxyCacheRoutineHourOfDay] 24')
03-JUN-2002  MGD  bugfix; ensure sys$search() RMS channel is released
15-MAY-2002  MGD  proxy gateway statistics
11-APR-2002  MGD  make a reactive purge initially more agressive,
                  bugfix; switch return not break with next reactive scan
04-APR-2002  MGD  add command-line and menu STOP to cache scans,
                  update admin and monitor status string with scan progress,
                  bugfix; command-line cache maintenance reporting
02-FEB-2002  MGD  rework POSTed query due to request body processing changes
22-SEP-2001  MGD  InstanceLock/UnLock() to control access to cache
04-AUG-2001  MGD  support module WATCHing
07-MAY-2001  MGD  monitor global section accounting changes
05-APR-2001  MGD  add boolean to prevent ProxyMaintCacheLock() lock status
                  block overwriting if multiple scans intiated from Admin Menu
20-DEC-2000  MGD  routine proxy maintainence optionally disabled/external
13-SEP-2000  MGD  ProxyMainReport() call refined to optionally provide
                  host name cache entries
08-JUL-2000  MGD  add VMS locking around cache scan (for clusters)
04-MAR-2000  MGD  use WriteFaol(), et.al.
03-JAN-2000  MGD  no changes required for ODS-5 compliance ... it's not
                  (see note in PROXYCACHE.C)
30-DEC-1999  MGD  change $GETDVI() to $GETDVIW() (potential bugfix)
04-DEC-1999  MGD  rework startup cache device and report details
22-OCT-1999  MGD  inaccessable cache device non-fatal during startup
20-JUN-1999  MGD  allow for cache devices >9GB in space calculations,
                  some refinement to statistics report
19-AUG-1998  MGD  initial development (recommenced DEC 1998)
*/
/*****************************************************************************/

#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 <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>

/* VMS related header files */
#include <atrdef.h>
#include <descrip.h>
#include <dvidef.h>
#include <fibdef.h>
#include <iodef.h>
#include <lkidef.h>
#include <lckdef.h>
#include <prvdef.h>
#include <rmsdef.h>
#include <ssdef.h>
#include <stsdef.h>

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

#define WASD_MODULE "PROXYMAINT"

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

char ErrorProxyMaintTooManyDevices [] = "Volume set has too many members.";

/* i.e. [000000...]*.HTC;* */
char  ProxyMaintScanFileSpec [] = PROXY_CACHE_SPEC;
int ProxyMaintScanFileSpecLength = sizeof(ProxyMaintScanFileSpec)-1;

BOOL  ProxyMaintChangeToReactive,
      ProxyMaintStopScan;

int  ProxyMaintAllocBlocks,
     ProxyMaintBackgroundFileCount,
     ProxyMaintBackgroundInterval,
     ProxyMaintBackgroundPurgeCount,
     ProxyMaintDeletedAllocBlocks,
     ProxyMaintDeletedCount,
     ProxyMaintDeletedUsedBlocks,
     ProxyMaintFileAllocBlocks,
     ProxyMaintFileCount,
     ProxyMaintFileUsedBlocks,
     ProxyMaintPurgeAtHour,
     ProxyMaintPurgeHoursIndex,
     ProxyMaintReactivePurgeCount,
     ProxyMaintResultFileNameLength,
     ProxyMaintRoutinePurgeCount,
     ProxyMaintRoutineHoursIndex,
     ProxyMaintScanType,
     ProxyMaintStatScanCount,
     ProxyMaintTargetPercent;
     ProxyMaintUsedBlocks;

unsigned long  ProxyMaintScanStartBinTime [2];

char  ProxyMaintEraseFileName [256],
      ProxyMaintExpandedFileName [256],
      ProxyMaintStatusStringBckGrnd [128] = "<I>none</I>",
      ProxyMaintStatusStringReactive [196] = "<I>none</I>",
      ProxyMaintStatusStringRoutine [128] = "<I>none</I>",
      ProxyMaintStatusStringStatScan [128] = "<I>none</I>",
      ProxyMaintResultFileName [256];

struct FAB  ProxyMaintSearchFab;
struct NAM  ProxyMaintSearchNam;

PROXY_CACHE_FILE_QIO  ProxyMaintCacheFileQio;

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

extern BOOL  ProxyCacheEnabled,
             ProxyCacheFreeSpaceAvailable,
             ProxyReportLog,
             ProxyReportCacheLog,
             ProxyServingEnabled,
             ProxyUnknownRequestFields;

extern int  EfnWait,
            EfnNoWait,
            HttpdTickSecond,
            OpcomMessages,
            ProxyCacheDeviceCheckMinutes,
            ProxyCacheDeviceMaxPercent,
            ProxyCacheDevicePurgePercent,
            ProxyCacheDeviceTargetPercent,
            ProxyCacheFileKBytesMax,
            ProxyCacheNoReloadSeconds,
            ProxyCachePurgeList[],
            ProxyCachePurgeListCount,
            ProxyCacheReloadList[],
            ProxyCacheRoutineHourOfDay,
            ProxyForwardedBy,
            ProxyHostLookupRetryCount,
            ProxyVerifyRecordMax,
            ProxyXForwardedFor;

extern char  CliProxyMaint[],
             ErrorSanityCheck[],
             ServerHostPort[],
             Utility[];

extern char  *ProxyCacheReloadListPtr,
             *ProxyCachePurgeListPtr;

extern unsigned short HttpdNumTime[];

extern ACCOUNTING_STRUCT  *AccountingPtr;
extern CONFIG_STRUCT  Config;
extern MSG_STRUCT  Msgs;
extern PROXY_ACCOUNTING_STRUCT  *ProxyAccountingPtr;
extern PROXYVERIFY_GBLSEC  *ProxyVerifyGblSecPtr;
extern WATCH_STRUCT  Watch;

/****************************************************************************/
/*
Initialize maintainence values.
*/

ProxyMaintInit ()

{
   int  status;

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

   if (WATCH_MODULE(WATCH_MOD_PROXY))
      WatchThis (NULL, FI_LI, WATCH_MOD_PROXY, "ProxyMaintInit()");

   ProxyCacheFreeSpaceAvailable =
      ProxyMaintDeviceFreeSpace (ProxyCacheDeviceMaxPercent);
   InstanceMutexLock (INSTANCE_MUTEX_HTTPD);
   ProxyAccountingPtr->FreeSpaceAvailable = ProxyCacheFreeSpaceAvailable;
   if (!ProxyAccountingPtr->StatusString[0] ||
       strstr (ProxyAccountingPtr->StatusString, "in-progress"))
      strcpy (ProxyAccountingPtr->StatusString, "(none)");
   InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);

   /* if space available schedule a purge, else if exceeded begin a purge */
   if (ProxyCacheFreeSpaceAvailable)
      ProxyMaintPrintDeviceStats (true);
   else
      ProxyMaintScanBegin (PROXY_MAINT_SCAN_REACTIVE);

   /* disable if not sensible */
   if (ProxyCacheRoutineHourOfDay < 0)
      ProxyCacheRoutineHourOfDay = -1;
   if (ProxyCacheRoutineHourOfDay > 24)
      ProxyCacheRoutineHourOfDay = -1;

   if (ProxyCacheRoutineHourOfDay == 24)
      WriteFaoStdout ("%!AZ-I-PROXYMAINT, background purge active\n", Utility);
   else
   if (ProxyCacheRoutineHourOfDay < 0)
      WriteFaoStdout ("%!AZ-I-PROXYMAINT, routine purge disabled\n", Utility);
}

/****************************************************************************/
/*
Initiate some proxy maintainance from the command line.
*/

ProxyMaintCli ()

{
   BOOL  ScanBackground,
         ScanRoutine,
         ScanReactive,
         ScanStatistics;
   int  ScanType;
   char  *cptr;

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

   if (WATCH_MODULE(WATCH_MOD_PROXY))
      WatchThis (NULL, FI_LI, WATCH_MOD_PROXY, "ProxyMaintCli()");

   ScanRoutine = ScanReactive = ScanStatistics = false;
   if ((ScanBackground = strsame (CliProxyMaint, "PURGE=BACKGROUND", 16)) ||
       (ScanRoutine = strsame (CliProxyMaint, "PURGE=ROUTINE", 13)) ||
       (ScanReactive = strsame (CliProxyMaint, "PURGE=REACTIVE", 14)) ||
       (ScanStatistics = strsame (CliProxyMaint, "CACHE=STATISTICS", 16)))
   {
      /* read the server configuration file (for OPCOM & proxy settings) */
      ConfigLoad (&Config);
      /* now configure up the proxy settings (disabled is OK here) */
      ProxyCacheInit (false);
      /* we'll assume it's always enabled */
      ProxyCacheEnabled = true;
      /* for CLI maintainance we always want messages on SYS$OUTPUT  */
      ProxyReportLog = true;

      if (ScanBackground)
      {
         /* override the configuration purge list if CLI one provided */
         if (CliProxyMaint[16] == '=')
            ProxyCachePurgeListPtr = CliProxyMaint + 16;
         ScanType = PROXY_MAINT_SCAN_BCKGRND;
      }
      else
      if (ScanRoutine)
      {
         /* override the configuration purge list if CLI one provided */
         if (CliProxyMaint[13] == '=')
            ProxyCachePurgeListPtr = CliProxyMaint + 14;
         ScanType = PROXY_MAINT_SCAN_ROUTINE;
      }
      else
      if (ScanReactive)
      {
         /* override the configuration purge list if CLI one provided */
         if (CliProxyMaint[14] == '=')
         {
            cptr = ProxyCachePurgeListPtr = CliProxyMaint + 15;
            while (*cptr && *cptr != '%') cptr++;
            if (*cptr && cptr > ProxyCachePurgeListPtr)
            {
               ProxyCacheDeviceTargetPercent = atoi(ProxyCachePurgeListPtr);
               if (ProxyCacheDeviceTargetPercent <= 0 ||
                   ProxyCacheDeviceTargetPercent >= 100)
               {
                  WriteFaoStdout ("%!AZ-I-PROXYMAINT, device percentage problem\n",
                                  Utility);
                  exit (STS$K_ERROR | STS$M_INHIB_MSG);
               }
               while (*cptr && !isdigit(*cptr)) cptr++;
               ProxyCachePurgeListPtr = cptr;
            }
         }
         ScanType = PROXY_MAINT_SCAN_REACTIVE;
      }
      else
      if (ScanStatistics)
      {
         ProxyCachePurgeListPtr = NULL;
         ScanType = PROXY_MAINT_SCAN_STATISTICS;
      }

      if (ProxyCachePurgeListPtr)
      {
         ProxyCacheInitPurgeList ();
         if (ProxyCachePurgeListPtr[0] == '*')
         {
            WriteFaoStdout ("%!AZ-I-PROXYMAINT, purge list problem\n", Utility);
            exit (STS$K_ERROR | STS$M_INHIB_MSG);
         }
      }

      /* if the scan starts OK then hibernate waiting for it to finish */
      if (VMSok (ProxyMaintScanBegin (ScanType))) sys$hiber ();

      exit (SS$_NORMAL);
   }

   fprintf (stdout, "%%%s-E-IVKEYW, unrecognized keyword\n \\%s\\\n",
            Utility, CliProxyMaint);
   exit (STS$K_ERROR | STS$M_INHIB_MSG);
}

/****************************************************************************/
/*
Print cache device statistics to stdout.
*/

ProxyMaintPrintDeviceStats (BOOL PrintToLog)

{
   int  status,
        ErrorCount,
        FreeBlocks,
        FreeMBytes,
        FreePercent,
        TotalMBytes,
        TotalBlocks,
        UsedBlocks,
        UsedMBytes,
        UsedPercent;
   char  *DevNamePtr;

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

   if (WATCH_MODULE(WATCH_MOD_PROXY))
      WatchThis (NULL, FI_LI, WATCH_MOD_PROXY,
                 "ProxyMaintPrintDeviceStats() !UL", PrintToLog);

   status = ProxyMaintDeviceStats (&DevNamePtr, &TotalBlocks,
                                   &UsedBlocks, &FreeBlocks, &ErrorCount);
   if (VMSok (status))
   {
      TotalMBytes = PROXY_MAINT_DEVICE_MBYTES(TotalBlocks);
      UsedMBytes = PROXY_MAINT_DEVICE_MBYTES(UsedBlocks);
      FreeMBytes = PROXY_MAINT_DEVICE_MBYTES(FreeBlocks);
      UsedPercent = PROXY_MAINT_DEVICE_PERCENT_USED(TotalBlocks,FreeBlocks);
      FreePercent = PROXY_MAINT_DEVICE_PERCENT_FREE(TotalBlocks,FreeBlocks);
   }
   else
      TotalBlocks = TotalMBytes = UsedBlocks = UsedMBytes =
         UsedPercent = FreeBlocks = FreeMBytes = FreePercent = 0;
      
   if (PrintToLog || ProxyReportLog)
      WriteFaoStdout (
"%!AZ-I-PROXYMAINT, cache device !AZ, !UL error!%s, !&S\n\
!UL blocks (!ULMB), !UL used (!ULMB !UL%), !UL free (!ULMB !UL%)\n",
         Utility, DevNamePtr, ErrorCount, status,
         TotalBlocks, TotalMBytes,
         UsedBlocks, UsedMBytes, UsedPercent,
         FreeBlocks, FreeMBytes, FreePercent);

   if (OpcomMessages & OPCOM_PROXY_MAINT)
      WriteFaoOpcom (
"%!AZ-I-PROXYMAINT, cache device !AZ, !&S\r\n\
!UL blocks (!ULMB), !UL used (!ULMB !UL%), !UL free (!ULMB !UL%)",
         Utility, DevNamePtr, status,
         TotalBlocks, TotalMBytes,
         UsedBlocks, UsedMBytes, UsedPercent,
         FreeBlocks, FreeMBytes, FreePercent);
}

/****************************************************************************/
/*
Include cache device statistics in WATCH report.
*/

ProxyMaintWatchDeviceStats ()

{
   int  status,
        ErrorCount,
        FreeBlocks,
        FreeMBytes,
        FreePercent,
        TotalMBytes,
        TotalBlocks,
        UsedBlocks,
        UsedMBytes,
        UsedPercent;
   char  *DevNamePtr;

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

   if (WATCH_MODULE(WATCH_MOD_PROXY))
      WatchThis (NULL, FI_LI, WATCH_MOD_PROXY,
                 "ProxyMaintWatchDeviceStats()");

   status = ProxyMaintDeviceStats (&DevNamePtr, &TotalBlocks,
                                   &UsedBlocks, &FreeBlocks, &ErrorCount);
   if (VMSok (status))
   {
      TotalMBytes = PROXY_MAINT_DEVICE_MBYTES(TotalBlocks);
      UsedMBytes = PROXY_MAINT_DEVICE_MBYTES(UsedBlocks);
      FreeMBytes = PROXY_MAINT_DEVICE_MBYTES(FreeBlocks);
      UsedPercent = PROXY_MAINT_DEVICE_PERCENT_USED(TotalBlocks,FreeBlocks);
      FreePercent = PROXY_MAINT_DEVICE_PERCENT_FREE(TotalBlocks,FreeBlocks);
   }
   else
      TotalBlocks = TotalMBytes = UsedBlocks = UsedMBytes =
         UsedPercent = FreeBlocks = FreeMBytes = FreePercent = 0;

#if WATCH_CAT

   WatchDataFormatted (
"!AZ !UL error!%s !&S \
!UL blocks (!ULMB), !UL used (!ULMB !UL%), !UL free (!ULMB !UL%)\n", 
      DevNamePtr, ErrorCount, status,
      TotalBlocks, TotalMBytes,
      UsedBlocks, UsedMBytes, UsedPercent,
      FreeBlocks, FreeMBytes, FreePercent);

#endif /* WATCH_CAT */
}

/****************************************************************************/
/*
Return information about file system space on the proxy cache device.  Will
function correctly with volume sets of up to eight members.  Returns a VMS
status code that should be checked for success.
*/

int ProxyMaintDeviceStats
(
char **DevNamePtrPtr,
int *TotalBlocksPtr,
int *UsedBlocksPtr,
int *FreeBlocksPtr,
int *ErrorCountPtr
)
{
#define PROXY_MAINT_CACHE_DEVICE_MAX 8

   static int  DeviceCount,
               ErrCnt,
               FreeBlocks,
               MaxBlock,
               VolCount;
   static unsigned short  Length;
   static short  DevChannel [PROXY_MAINT_CACHE_DEVICE_MAX];
   static char  CacheDevName [65],
                DevName [65] = PROXY_CACHE_DEVICE;
   static $DESCRIPTOR (DevNameDsc, "");
   static struct {
      short BufferLength;
      short ItemCode;
      void  *BufferPtr;
      void  *LengthPtr;
   }
   DevNamItemList [] = 
   {
      { sizeof(CacheDevName), DVI$_DEVNAM, &CacheDevName, &Length },
      { sizeof(VolCount), DVI$_VOLCOUNT, &VolCount, 0 },
      { 0, 0, 0, 0 }
   },
   NextDevNamItemList [] = 
   {
      { sizeof(DevName), DVI$_NEXTDEVNAM, &DevName, &Length },
      { 0, 0, 0, 0 }
   },
   BlocksItemList [] = 
   {
      { sizeof(MaxBlock), DVI$_MAXBLOCK, &MaxBlock, 0 },
      { sizeof(FreeBlocks), DVI$_FREEBLOCKS, &FreeBlocks, 0 },
      { sizeof(ErrCnt), DVI$_ERRCNT, &ErrCnt, 0 },
      { 0, 0, 0, 0 }
   };

   int  idx,
        status,
        ErrorCount,
        TotalBlocks,
        TotalFreeBlocks,
        TotalUsedBlocks;
   IO_SB  IOsb;

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

   if (WATCH_MODULE(WATCH_MOD_PROXY))
      WatchThis (NULL, FI_LI, WATCH_MOD_PROXY, "ProxyMaintDeviceStats()");

   if (DevNamePtrPtr) *DevNamePtrPtr = DevName;
   if (TotalBlocksPtr) *TotalBlocksPtr = 0;
   if (UsedBlocksPtr) *UsedBlocksPtr = 0;
   if (FreeBlocksPtr) *FreeBlocksPtr = 0;
   if (ErrorCountPtr) *ErrorCountPtr = 0;

   if (!DeviceCount)
   {
      /**************/
      /* initialize */
      /**************/

      DevNameDsc.dsc$w_length = strlen(PROXY_CACHE_ROOT);
      DevNameDsc.dsc$a_pointer = PROXY_CACHE_ROOT;

      /* assign a channel to the cache device (or primary if a volume set) */
      if (VMSnok (status =
          sys$assign (&DevNameDsc, &DevChannel[DeviceCount], 0, 0)))
         return (status);

      DeviceCount++;

      status = sys$getdviw (EfnWait, DevChannel[0], 0,
                            &DevNamItemList, &IOsb, 0, 0, 0);
      if (VMSok (status)) status = IOsb.Status;
      if (VMSnok (status)) return (status);

      CacheDevName[Length] = '\0';
      if (CacheDevName[0] == '_')
         memmove (CacheDevName, CacheDevName+1, Length);

      /* loop assigning a channel to all devices in volume set (if it is!) */
      while (--VolCount)
      {
         if (DeviceCount >= PROXY_MAINT_CACHE_DEVICE_MAX)
            ErrorExitVmsStatus (0, ErrorProxyMaintTooManyDevices, FI_LI);

         status = sys$getdviw (EfnWait, DevChannel[0], 0,
                               &NextDevNamItemList, &IOsb, 0, 0, 0);
         if (VMSok (status)) status = IOsb.Status;
         if (VMSnok (status)) return (status);

         DevName[Length] = '\0';

         if (!Length) break;

         DevNameDsc.dsc$w_length = Length;
         DevNameDsc.dsc$a_pointer = DevName;

         if (VMSnok (status =
             sys$assign (&DevNameDsc, &DevChannel[DeviceCount++], 0, 0)))
            return (status);
      }
   }

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

   ErrorCount = TotalBlocks = TotalFreeBlocks = 0;

   for (idx = 0; idx < DeviceCount; idx++)
   {
      status = sys$getdviw (EfnWait, DevChannel[idx], 0,
                            &BlocksItemList, &IOsb, 0, 0, 0);
      if (VMSok (status)) status = IOsb.Status;
      if (VMSnok (status)) return (status);

      TotalBlocks += MaxBlock;
      TotalFreeBlocks += FreeBlocks;
      ErrorCount += ErrCnt;
   }

   TotalUsedBlocks = TotalBlocks - TotalFreeBlocks;

   if (WATCH_MODULE(WATCH_MOD_PROXY))
      WatchThis (NULL, FI_LI, WATCH_MOD_PROXY,
                 "!UL !UL !UL !ULMB !ULMB !ULMB !UL% !UL%",
                  TotalBlocks, TotalFreeBlocks, TotalUsedBlocks,
                  TotalBlocks >> 11, TotalFreeBlocks >> 11,
                  TotalUsedBlocks >> 11,
                  TotalFreeBlocks*100/TotalBlocks, 
                  TotalUsedBlocks*100/TotalBlocks);

   if (DevNamePtrPtr) *DevNamePtrPtr = CacheDevName;
   if (TotalBlocksPtr) *TotalBlocksPtr = TotalBlocks;
   if (UsedBlocksPtr) *UsedBlocksPtr = TotalUsedBlocks;
   if (FreeBlocksPtr) *FreeBlocksPtr = TotalFreeBlocks;
   if (ErrorCountPtr) *ErrorCountPtr = ErrorCount;

   return (SS$_NORMAL);
}

/****************************************************************************/
/*
If the percentage of used space on the cache device is greater than or equal to
the specified percentage then return true, otherwise return false.
*/

BOOL ProxyMaintDeviceFreeSpace (int LimitPercent)

{
   int  status,
        FreeBlocks,
        UsedPercent,
        TotalBlocks;

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

   if (WATCH_MODULE(WATCH_MOD_PROXY))
      WatchThis (NULL, FI_LI, WATCH_MOD_PROXY,
                 "ProxyMaintDeviceFreeSpace() !UL", LimitPercent);

   if (VMSok (status =
       ProxyMaintDeviceStats (NULL, &TotalBlocks, NULL, &FreeBlocks, NULL)))
   {
      UsedPercent = PROXY_MAINT_DEVICE_PERCENT_USED(TotalBlocks,FreeBlocks);

      if (UsedPercent > LimitPercent)
         return (false);
      else
         return (true);
   }
   else
   {
      WriteFaoStdout (
"%!AZ-W-PROXYMAINT, !20%D, cache device problem - PROXY CACHING DISABLED\n\
-!&M\n",
         Utility, 0, status);

      if (OpcomMessages & OPCOM_PROXY_MAINT)
         WriteFaoOpcom (
"%!AZ-W-PROXYMAINT, cache device problem - PROXY CACHING DISABLED\r\n\
-!&M",
            Utility, status);

      /* fudge ... indicate sufficient space, suppresses any purging */
      return (true);
   }
}

/*****************************************************************************/
/*
Called by HttpdTick() every minute (or so).   This routine schedules the
approriate proxy cache purge management activities.
*/

ProxyMaintSupervisor ()

{
   static int  SupervisorSecond,
               RoutineYear,
               RoutineMonth,
               RoutineDay;

   int  status;

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

   if (WATCH_MODULE(WATCH_MOD_PROXY))
      WatchThis (NULL, FI_LI, WATCH_MOD_PROXY, "ProxyMaintSupervisor()");

   if (!ProxyServingEnabled) return;

   if (SupervisorSecond < HttpdTickSecond)
   {
      /******************/
      /* reactive purge */
      /******************/

      ProxyCacheFreeSpaceAvailable =
         ProxyMaintDeviceFreeSpace (ProxyCacheDeviceMaxPercent);
      InstanceMutexLock (INSTANCE_MUTEX_HTTPD);
      ProxyAccountingPtr->FreeSpaceAvailable = ProxyCacheFreeSpaceAvailable;
      InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);

      if (WATCH_CATEGORY(WATCH_PROXY_CACHE_MNT))
      {
         if (ProxyCacheFreeSpaceAvailable)
            WatchThis (NULL, FI_LI, WATCH_PROXY_CACHE_MNT,
                       "MAINT sufficient free SPACE !UL% max",
                       ProxyCacheDeviceMaxPercent);
         else
            WatchThis (NULL, FI_LI, WATCH_PROXY_CACHE_MNT,
                       "MAINT INSUFFICIENT free SPACE !UL% max",
                       ProxyCacheDeviceMaxPercent);
         ProxyMaintWatchDeviceStats ();
      }

      /* if insufficient free space */
      if (!ProxyCacheFreeSpaceAvailable)
      {
         if (ProxyMaintScanType == PROXY_MAINT_SCAN_NONE)
         {
            /* can purge now */
            ProxyMaintScanBegin (PROXY_MAINT_SCAN_REACTIVE);
         }
         else
         if (ProxyMaintScanType == PROXY_MAINT_SCAN_BCKGRND)
         {
            /* after background has stopped */
            ProxyMaintChangeToReactive = true;
         }
      }
   }

   if (ProxyCacheRoutineHourOfDay >= 0 &&
       ProxyCacheRoutineHourOfDay <= 23)
   {
      /*****************/
      /* routine purge */
      /*****************/

      /* only if during the first five minutes of that hour */
      if (ProxyCacheRoutineHourOfDay == HttpdNumTime[3] &&
          (RoutineDay != HttpdNumTime[2] ||
           RoutineMonth != HttpdNumTime[1] ||
           RoutineYear != HttpdNumTime[0]) &&
          HttpdNumTime[4] <= 5)
      {
         RoutineYear = HttpdNumTime[0];
         RoutineMonth = HttpdNumTime[1];
         RoutineDay = HttpdNumTime[2];

         /* if no other scan is underway then start the routine one */
         if (ProxyMaintScanType == PROXY_MAINT_SCAN_NONE)
            ProxyMaintScanBegin (PROXY_MAINT_SCAN_ROUTINE);
      }
   }
   else
   if (ProxyCacheRoutineHourOfDay == 24)
   {
      /********************/
      /* background purge */
      /********************/

      /* check for background purge at same time and rate as reactive  */
      if (SupervisorSecond < HttpdTickSecond)
      {
         /* if another scan is not already underway start one */
         if (ProxyMaintScanType == PROXY_MAINT_SCAN_NONE)
            ProxyMaintScanBegin (PROXY_MAINT_SCAN_BCKGRND);
      }
   }

   if (SupervisorSecond < HttpdTickSecond)
      SupervisorSecond = HttpdTickSecond +
                         ProxyCacheDeviceCheckMinutes * 60;
}

/*****************************************************************************/
/*
Queue a wait for the apprpriate milliseconds before continuing on to the next
file in the cache. There are 86400 seconds in a 24 hour period.  At one per
second we can therefore scan 86400 files in that day (more or less).  At two
per second 172,800 per day.  At four per second 345,600 files per day.  And so
on.  Returns false if ProxyMaintScanSearch() should continue with the file
scan, true if it should wait.  When the timer expires the AST calls
ProxyMaintScanSearch() to continue with the scan.
*/

BOOL ProxyMaintBackgroundTimer ()

{
   static BOOL  FlipFlop;
   static int  mSec,
               PrevFileCount = -1;
   static unsigned long  DelayDelta [2];

   static struct RateTableStruct
   {
      int  min,
           max,
           mSec;
   }
   RateTable [] = 
   {
      {       0,    4320,  250 },  /* the default rate (4 per second) */
      {    4321,   33200, 2000 },  /* 1 every 2 seconds */
      {   33201,   86400, 1000 },  /* 1 per second */
      {   86401,  172800,  500 },  /* 2 per second */
      {  172801,  259200,  333 },  /* 3 per second */
      {  259201,  345600,  250 },  /* 4 per second */
      {  345601,  432000,  200 },  /* 5 per second */
      {  432001,  518400,  167 },  /* 6 per second */
      {  518401,  604800,  143 },  /* 7 per second */
      {  604801,  691200,  125 },  /* 8 per second */
      {  691201,  777600,  111 },  /* 9 per second */
      {  777601,  864000,  100 },  /* 10 per second */
      {  864001, 1728000,   50 },  /* 20 per second */
      { 1728001, 3456000,   25 },  /* 40, highly unlikely :^) */
      {       0,       0,    0 }   /* sentinal */
   };

   int  idx, status;

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

   if (WATCH_MODULE(WATCH_MOD_PROXY))
      WatchThis (NULL, FI_LI, WATCH_MOD_PROXY,
                 "ProxyMaintBackgroundTimer() !&B !UL", FlipFlop, mSec);

   /* only every alternate call delays */
   if (FlipFlop = !FlipFlop) return (false);

   if (PrevFileCount != ProxyMaintBackgroundFileCount)
   {
      PrevFileCount = ProxyMaintBackgroundFileCount;

      /* scan through the rate table looking for a matching number of files */
      for (idx = 0; RateTable[idx].mSec; idx++)
         if (ProxyMaintBackgroundFileCount >= RateTable[idx].min &&
             ProxyMaintBackgroundFileCount < RateTable[idx].max)
            break;
      mSec = ProxyMaintBackgroundInterval = RateTable[idx].mSec;

      if (WATCH_MODULE(WATCH_MOD_PROXY))
         WatchThis (NULL, FI_LI, WATCH_MOD_PROXY,
                    "!UL >= !UL < !UL !SL mS",
                    RateTable[idx].min, ProxyMaintBackgroundFileCount,
                    RateTable[idx].max, mSec);

      /* this is one millisecond delta */
      DelayDelta[0] = -10000;
      DelayDelta[1] = -1;
      /* multiply by the number of milliseconds between each file */
      if (VMSnok (status = lib$mult_delta_time (&mSec, &DelayDelta)))
         ErrorExitVmsStatus (status, "lib$mult_delta_time()", FI_LI);
   }

   /* if matching rate not found (exceeds maximum) go like the clappers */
   if (!mSec) return (false);

   /* queue up a timer event delaying the next file check */
   if (VMSnok (status =
       sys$setimr (0, &DelayDelta, &ProxyMaintScanSearch, 0, 0)))
      ErrorExitVmsStatus (status, "sys$setimr()", FI_LI);

   return (true);
}

/****************************************************************************/
/*
Initialize ready to begin a routine or purge pass.
*/

int ProxyMaintScanBegin (int ScanType)

{
   static BOOL  NotEnabledMessage;

   int  status;

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

   if (WATCH_MODULE(WATCH_MOD_PROXY))
      WatchThis (NULL, FI_LI, WATCH_MOD_PROXY,
                 "ProxyMaintScanBegin() !UL", ScanType);

   if (!ProxyCacheEnabled)
   {
      if (NotEnabledMessage) return (STS$K_ERROR);
      NotEnabledMessage = true;
      if (ProxyReportLog)
         WriteFaoStdout ("%!AZ-W-PROXYMAINT, !20%D, cache not enabled\n",
                         Utility, 0);
      return (STS$K_ERROR);
   }
   NotEnabledMessage = false;

   if (ScanType == PROXY_MAINT_SCAN_REACTIVE &&
       ProxyMaintScanType == PROXY_MAINT_SCAN_BCKGRND)
   {
      /****************************/
      /* after background stopped */
      /****************************/

      ProxyMaintChangeToReactive = true;
      return (SS$_NORMAL);
   }

   if (ScanType == PROXY_MAINT_SCAN_STOP)
   {
      /************/
      /* whooaaa! */
      /************/

      ProxyMaintStopScan = true;
      return (SS$_NORMAL);
   }

   /* attempt to obtain the cache scan lock */
   status = InstanceLockNoWait (INSTANCE_CLUSTER_PROXYMAINT);
   if (status == SS$_NOTQUEUED)
   {
      if (ProxyMaintScanType == PROXY_MAINT_SCAN_BCKGRND)
      {
         /* background scans don't report such startup failures */
         return (STS$K_ERROR);
      }
      if (ProxyReportLog)
         WriteFaoStdout ("%!AZ-W-PROXYMAINT, !20%D, cache already locked\n",
                         Utility, &ProxyMaintScanStartBinTime);
      if (OpcomMessages & OPCOM_PROXY_MAINT)
         WriteFaoOpcom ("%!AZ-W-PROXYMAINT, cache already locked",
                        Utility);
      return (STS$K_ERROR);
   }
   if (VMSnok (status))
      ErrorExitVmsStatus (status, "InstanceLock()", FI_LI);

   /* only useful after the scan has started ;^) */
   ProxyMaintStopScan = false;

   sys$gettim (&ProxyMaintScanStartBinTime);

   ProxyMaintScanType = ScanType;
   ProxyMaintTargetPercent = ProxyCacheDeviceTargetPercent;

   ProxyMaintPurgeHoursIndex = 0;
   /* if reactive purge we start a little more aggressively */
   if (ProxyMaintScanType == PROXY_MAINT_SCAN_REACTIVE)
      if (ProxyMaintPurgeHoursIndex < ProxyCachePurgeListCount)
         ProxyMaintPurgeHoursIndex++;
   ProxyMaintPurgeAtHour = ProxyCachePurgeList[ProxyMaintPurgeHoursIndex];

   ProxyMaintDeletedCount =
      ProxyMaintDeletedAllocBlocks =
      ProxyMaintDeletedUsedBlocks =
      ProxyMaintAllocBlocks =
      ProxyMaintUsedBlocks = 0;

   switch (ProxyMaintScanType)
   {
      case PROXY_MAINT_SCAN_STATISTICS :

         /****************************/
         /* getting cache statistics */
         /****************************/

         if (WATCH_CATEGORY(WATCH_PROXY_CACHE_MNT))
         {
             WatchThis (NULL, FI_LI, WATCH_PROXY_CACHE_MNT,
                        "MAINT begin STATISTICS SCAN");
             ProxyMaintWatchDeviceStats ();
         }

         if (ProxyReportLog)
            WriteFaoStdout ("%!AZ-I-PROXYMAINT, !20%D, begin statistics scan\n",
                            Utility, &ProxyMaintScanStartBinTime);

         if (OpcomMessages & OPCOM_PROXY_MAINT)
            WriteFaoOpcom ("%!AZ-I-PROXYMAINT, begin statistics scan",
                           Utility);

         ProxyMaintPrintDeviceStats (false);
         ProxyMaintStatScanCount++;
         ProxyMaintScanNow ();
         return (SS$_NORMAL);

      case PROXY_MAINT_SCAN_REACTIVE :

         /********************************/
         /* purging to create free space */
         /********************************/

         if (WATCH_CATEGORY(WATCH_PROXY_CACHE_MNT))
         {
             WatchThis (NULL, FI_LI, WATCH_PROXY_CACHE_MNT,
"MAINT begin REACTIVE PURGE (>!UL hours, !UL% max, !UL% target)",
                ProxyMaintPurgeAtHour,  ProxyCacheDeviceMaxPercent,
                ProxyMaintTargetPercent);
             ProxyMaintWatchDeviceStats ();
         }

         if (ProxyReportLog)
            WriteFaoStdout (
"%!AZ-I-PROXYMAINT, !20%D, begin reactive purge\n\
(>!UL hours, !UL% max, !UL% target)\n",
               Utility, &ProxyMaintScanStartBinTime,
               ProxyMaintPurgeAtHour,  ProxyCacheDeviceMaxPercent,
               ProxyMaintTargetPercent);

         if (OpcomMessages & OPCOM_PROXY_MAINT)
            WriteFaoOpcom (
"%!AZ-I-PROXYMAINT, begin reactive purge\r\n\
(>!UL hours, !UL% max, !UL% target)",
                Utility,
                ProxyMaintPurgeAtHour,  ProxyCacheDeviceMaxPercent,
                ProxyMaintTargetPercent);

         ProxyMaintPrintDeviceStats (false);
         ProxyMaintReactivePurgeCount++;
         ProxyMaintScanNow ();
         return (SS$_NORMAL);

      case PROXY_MAINT_SCAN_ROUTINE :

         /*******************/
         /* routine cleanup */
         /*******************/

         if (WATCH_CATEGORY(WATCH_PROXY_CACHE_MNT))
         {
             WatchThis (NULL, FI_LI, WATCH_PROXY_CACHE_MNT,
                "MAINT begin ROUTINE PURGE (>!UL hours)",
                ProxyMaintPurgeAtHour);
             ProxyMaintWatchDeviceStats ();
         }

         if (ProxyReportLog)
            WriteFaoStdout (
"%!AZ-I-PROXYMAINT, !20%D, begin routine purge (>!UL hours)\n",
               Utility, &ProxyMaintScanStartBinTime, ProxyMaintPurgeAtHour);

         if (OpcomMessages & OPCOM_PROXY_MAINT)
            WriteFaoOpcom (
"%!AZ-I-PROXYMAINT, begin routine purge (>!UL hours)",
               Utility, ProxyMaintPurgeAtHour);

         ProxyMaintPrintDeviceStats (false);
         ProxyMaintRoutinePurgeCount++;
         ProxyMaintScanNow ();
         return (SS$_NORMAL);

      case PROXY_MAINT_SCAN_BCKGRND :

         /**********************/
         /* background cleanup */
         /**********************/

         if (WATCH_CATEGORY(WATCH_PROXY_CACHE_MNT))
         {
             WatchThis (NULL, FI_LI, WATCH_PROXY_CACHE_MNT,
                "MAINT begin BACKGROUND PURGE (>!UL hours)",
                ProxyMaintPurgeAtHour);
             ProxyMaintWatchDeviceStats ();
         }

         if (ProxyReportLog)
            WriteFaoStdout (
"%!AZ-I-PROXYMAINT, !20%D, begin background purge (>!UL hours)\n",
               Utility, &ProxyMaintScanStartBinTime, ProxyMaintPurgeAtHour);

         if (OpcomMessages & OPCOM_PROXY_MAINT)
            WriteFaoOpcom (
"%!AZ-I-PROXYMAINT, begin background purge (>!UL hours)",
               Utility, ProxyMaintPurgeAtHour);

         InstanceMutexLock (INSTANCE_MUTEX_HTTPD);
         ProxyMaintBackgroundFileCount = ProxyAccountingPtr->PrevFileCount;
         InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);

         ProxyMaintPrintDeviceStats (false);
         ProxyMaintBackgroundPurgeCount++;
         ProxyMaintScanNow ();
         return (SS$_NORMAL);
   }

   ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI);
}

/****************************************************************************/
/*
End of a single pass through the entire cache directory tree.  A PURGE will be
made up of multiple passes, each using the next access hours found in the
'ProxyCachePurgeList' array.  A ROUTINE purge will only make the one pass
though the tree using only the hours in the zero element.
*/

ProxyMaintScanEnd ()

{
   int  status,
        FreeBlocks,
        FreePercent,
        TotalBlocks;
   unsigned long  CurrentTime [2],
                  DeltaTime [2];
   char  *cptr;
   PROXY_CACHE_FILE_QIO  *cfqptr;

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

   if (WATCH_MODULE(WATCH_MOD_PROXY))
      WatchThis (NULL, FI_LI, WATCH_MOD_PROXY, "ProxyMaintScanEnd()");

   /* first ensure release of search channel by doing a syntax-only parse */
   ProxyMaintSearchFab.fab$l_fna = "a:[b]c.d;";
   ProxyMaintSearchFab.fab$b_fns = 9;
   ProxyMaintSearchFab.fab$b_dns = 0;
   ProxyMaintSearchFab.fab$l_fop &= ~FAB$M_ASY;

   ProxyMaintSearchNam.nam$l_esa = ProxyMaintExpandedFileName;
   ProxyMaintSearchNam.nam$b_ess = sizeof(ProxyMaintExpandedFileName)-1;
   ProxyMaintSearchNam.nam$l_rlf = 0;
   ProxyMaintSearchNam.nam$b_nop = NAM$M_SYNCHK;

   status = sys$parse (&ProxyMaintSearchFab, 0, 0);

   cfqptr = &ProxyMaintCacheFileQio;
   if (cfqptr->AcpChannel)
   {
      sys$dassgn (cfqptr->AcpChannel);
      cfqptr->AcpChannel = 0;
   }

   if (ProxyMaintStopScan)
   {
      cptr = "&nbsp; <B>(manually STOPed)</B>";

      if (ProxyReportLog)
         WriteFaoStdout (
            "%!AZ-I-PROXYMAINT, !20%D, cache scan manually STOPed\n",
            Utility, 0);

      if (OpcomMessages & OPCOM_PROXY_MAINT)
         WriteFaoOpcom ("%!AZ-I-PROXYMAINT, cache scan manually STOPed",
                        Utility);

      ProxyCacheFreeSpaceAvailable =
         ProxyMaintDeviceFreeSpace (ProxyCacheDeviceMaxPercent);
   }
   else
   {
      cptr = "";
      ProxyCacheFreeSpaceAvailable =
         ProxyMaintDeviceFreeSpace (ProxyMaintTargetPercent);
   }

   InstanceMutexLock (INSTANCE_MUTEX_HTTPD);
   ProxyAccountingPtr->FreeSpaceAvailable = ProxyCacheFreeSpaceAvailable;
   InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);

   sys$gettim (&CurrentTime);
   lib$sub_times (&CurrentTime, &ProxyMaintScanStartBinTime, &DeltaTime);

   switch (ProxyMaintScanType)
   {
      case PROXY_MAINT_SCAN_STATISTICS :

         /*******************/
         /* cach statistics */
         /*******************/

         if (WATCH_CATEGORY(WATCH_PROXY_CACHE_MNT))
         {
            WatchThis (NULL, FI_LI, WATCH_PROXY_CACHE_MNT,
               "MAINT end STATISTICS SCAN !UL files (!UL/!UL blocks)",
               ProxyMaintFileCount, ProxyMaintUsedBlocks,
               ProxyMaintAllocBlocks);
            ProxyMaintWatchDeviceStats ();
         }

         if (ProxyReportLog)
            WriteFaoStdout (
"%!AZ-I-PROXYMAINT, !20%D, end statistics scan\n\
!UL files (!UL/!UL)\n",
               Utility, 0,
               ProxyMaintFileCount, ProxyMaintUsedBlocks,
               ProxyMaintAllocBlocks);

         if (OpcomMessages & OPCOM_PROXY_MAINT)
            WriteFaoOpcom (
"%!AZ-I-PROXYMAINT, end statistics scan\r\n\
!UL files (!UL/!UL)",
               Utility,
               ProxyMaintFileCount, ProxyMaintUsedBlocks,
               ProxyMaintAllocBlocks);
         ProxyMaintPrintDeviceStats (false);

         WriteFao (ProxyMaintStatusStringStatScan,
                   sizeof(ProxyMaintStatusStringStatScan), NULL,
                   "!UL, !20%D (!%T)<BR>!UL files (!UL/!UL)!AZ",
                   ProxyMaintStatScanCount,
                   &ProxyMaintScanStartBinTime, &DeltaTime,
                   ProxyMaintFileCount, ProxyMaintUsedBlocks,
                   ProxyMaintAllocBlocks, cptr);
         {
            /* don't use WASD functions to write into the locked areas */
            $DESCRIPTOR (FaoDsc, "!AZ, !UL files (!UL/!UL)\0");
            $DESCRIPTOR (StringDsc, ""); 

            StringDsc.dsc$w_length =
               sizeof(ProxyAccountingPtr->StatusString)-1;
            StringDsc.dsc$a_pointer = ProxyAccountingPtr->StatusString;
            InstanceMutexLock (INSTANCE_MUTEX_HTTPD);
            sys$fao (&FaoDsc, NULL, &StringDsc,
                     DigitDayTime(0), ProxyMaintFileCount,
                     ProxyMaintUsedBlocks, ProxyMaintAllocBlocks);
            InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);
         }

         break;

      case PROXY_MAINT_SCAN_REACTIVE :

         /********************************/
         /* purging to create free space */
         /********************************/

         /* if reached desired percent, or one complete purge, or no files */
         if (ProxyCacheFreeSpaceAvailable ||
             ProxyMaintPurgeHoursIndex >= ProxyCachePurgeListCount ||
             !ProxyMaintFileCount ||
             ProxyMaintStopScan)
         {
            /*****************/
            /* cease purging */
            /*****************/

            if (WATCH_CATEGORY(WATCH_PROXY_CACHE_MNT))
            {
               WatchThis (NULL, FI_LI, WATCH_PROXY_CACHE_MNT,
"MAINT end REACTIVE PURGE \
!UL files (!UL/!UL blocks) !UL deleted (!UL/!UL blocks)",
                  ProxyMaintFileCount, ProxyMaintUsedBlocks,
                  ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
                  ProxyMaintDeletedUsedBlocks, ProxyMaintDeletedAllocBlocks);
            }

            if (ProxyReportLog)
               WriteFaoStdout (
"%!AZ-I-PROXYMAINT, !20%D, end reactive purge\n\
!UL files (!UL/!UL), !UL deleted (!UL/!UL)\n",
                  Utility, 0,
                  ProxyMaintFileCount, ProxyMaintUsedBlocks,
                  ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
                  ProxyMaintDeletedUsedBlocks,
                  ProxyMaintDeletedAllocBlocks);

            if (OpcomMessages & OPCOM_PROXY_MAINT)
               WriteFaoOpcom (
"%!AZ-I-PROXYMAINT, end reactive purge\r\n\
!UL files (!UL/!UL), !UL deleted (!UL/!UL)",
                  Utility,
                  ProxyMaintFileCount, ProxyMaintUsedBlocks,
                  ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
                  ProxyMaintDeletedUsedBlocks,
                  ProxyMaintDeletedAllocBlocks);
            ProxyMaintPrintDeviceStats (false);

            WriteFao (ProxyMaintStatusStringReactive,
                      sizeof(ProxyMaintStatusStringReactive), NULL, 
"!UL, !20%D (!%T)<BR>!UL files (!UL/!UL), !UL deleted (!UL/!UL)!AZ",
                      ProxyMaintReactivePurgeCount,
                      &ProxyMaintScanStartBinTime, &DeltaTime,
                      ProxyMaintFileCount, ProxyMaintUsedBlocks,
                      ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
                      ProxyMaintDeletedUsedBlocks,
                      ProxyMaintDeletedAllocBlocks, cptr);
            {
               /* don't use WASD functions to write into the locked areas */
               $DESCRIPTOR (FaoDsc,
"!AZ, !UL files (!UL/!UL), !UL deleted (!UL/!UL)\0");
               $DESCRIPTOR (StringDsc, ""); 

               StringDsc.dsc$w_length =
                  sizeof(ProxyAccountingPtr->StatusString)-1;
               StringDsc.dsc$a_pointer = ProxyAccountingPtr->StatusString;
               InstanceMutexLock (INSTANCE_MUTEX_HTTPD);
               sys$fao (&FaoDsc, NULL, &StringDsc,
                        DigitDayTime(0), ProxyMaintFileCount,
                        ProxyMaintUsedBlocks, ProxyMaintAllocBlocks,
                        ProxyMaintDeletedCount, ProxyMaintDeletedUsedBlocks,
                        ProxyMaintDeletedAllocBlocks);
               InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);
            }
            break;
         }

         /**********************/
         /* next pass of purge */
         /**********************/

         ProxyMaintPurgeAtHour =
            ProxyCachePurgeList[++ProxyMaintPurgeHoursIndex];

         if (WATCH_CATEGORY(WATCH_PROXY_CACHE_MNT))
         {
            WatchThis (NULL, FI_LI, WATCH_PROXY_CACHE_MNT,
"MAINT end pass REACTIVE PURGE \
!UL files (!UL/!UL blocks) !UL deleted (!UL/!UL blocks)",
               ProxyMaintFileCount, ProxyMaintUsedBlocks,
               ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
               ProxyMaintDeletedUsedBlocks, ProxyMaintDeletedAllocBlocks);

            WatchThis (NULL, FI_LI, WATCH_PROXY_CACHE_MNT,
               "MAINT next REACTIVE PURGE >!UL hours !UL% max !UL% target",
               ProxyMaintPurgeAtHour, ProxyCacheDeviceMaxPercent,
               ProxyMaintTargetPercent);

            ProxyMaintWatchDeviceStats ();
         }

         if (ProxyReportLog)
            WriteFaoStdout (
"%!AZ-I-PROXYMAINT, !20%D, end reactive pass\n\
!UL files (!UL/!UL), !UL deleted (!UL/!UL)\n\
next pass >!UL hours !UL% max !UL% target\n",
               Utility, 0,
               ProxyMaintFileCount, ProxyMaintUsedBlocks,
               ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
               ProxyMaintDeletedUsedBlocks,
               ProxyMaintDeletedAllocBlocks,
               ProxyMaintPurgeAtHour,  ProxyCacheDeviceMaxPercent,
               ProxyMaintTargetPercent);

         if (OpcomMessages & OPCOM_PROXY_MAINT)
            WriteFaoOpcom (
"%!AZ-I-PROXYMAINT, end reactive pass\r\n\
!UL files (!UL/!UL), !UL deleted (!UL/!UL)\r\n\
next pass >!UL hours !UL% max !UL% target",
               Utility,
               ProxyMaintFileCount, ProxyMaintUsedBlocks,
               ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
               ProxyMaintDeletedUsedBlocks,
               ProxyMaintDeletedAllocBlocks,
               ProxyMaintPurgeAtHour,  ProxyCacheDeviceMaxPercent,
               ProxyMaintTargetPercent);

         ProxyMaintPrintDeviceStats (false);
         {
            /* don't use WASD functions to write into the locked areas */
            $DESCRIPTOR (FaoDsc,
"!AZ, REACTIVE purge, >!UL hours, !UL% max, !UL% target\0");
            $DESCRIPTOR (StringDsc, ""); 

            StringDsc.dsc$w_length =
               sizeof(ProxyAccountingPtr->StatusString)-1;
            StringDsc.dsc$a_pointer = ProxyAccountingPtr->StatusString;
            InstanceMutexLock (INSTANCE_MUTEX_HTTPD);
            sys$fao (&FaoDsc, NULL, &StringDsc,
                     DigitDayTime(0), ProxyMaintPurgeAtHour,
                     ProxyCacheDeviceMaxPercent, ProxyMaintTargetPercent);
            InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);
         }

         /* reset these, we want to accumulate the deleted totals */
         ProxyMaintAllocBlocks = ProxyMaintUsedBlocks = 0;

         ProxyMaintScanNow ();

         /* we're doing another scan, so we don't break! */
         return;

      case PROXY_MAINT_SCAN_ROUTINE :

         /*******************/
         /* routine cleanup */
         /*******************/

         if (WATCH_CATEGORY(WATCH_PROXY_CACHE_MNT))
         {
            WatchThis (NULL, FI_LI, WATCH_PROXY_CACHE_MNT,
"MAINT end ROUTINE PURGE \
!UL files (!UL/!UL blocks) !UL deleted (!UL/!UL blocks)",
               ProxyMaintFileCount, ProxyMaintUsedBlocks,
               ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
               ProxyMaintDeletedUsedBlocks, ProxyMaintDeletedAllocBlocks);
            ProxyMaintWatchDeviceStats ();
         }

         if (ProxyReportLog)
            WriteFaoStdout (
"%!AZ-I-PROXYMAINT, !20%D, end routine purge\n\
!UL files (!UL/!UL), !UL deleted (!UL/!UL)\n",
               Utility, 0,
               ProxyMaintFileCount, ProxyMaintUsedBlocks,
               ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
               ProxyMaintDeletedUsedBlocks,
               ProxyMaintDeletedAllocBlocks);

         if (OpcomMessages & OPCOM_PROXY_MAINT)
            WriteFaoOpcom (
"%!AZ-I-PROXYMAINT, end routine purge\r\n\
!UL files (!UL/!UL), !UL deleted (!UL/!UL)",
               Utility,
               ProxyMaintFileCount, ProxyMaintUsedBlocks,
               ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
               ProxyMaintDeletedUsedBlocks,
               ProxyMaintDeletedAllocBlocks);

         ProxyMaintPrintDeviceStats (false);

         WriteFao (ProxyMaintStatusStringRoutine,
                   sizeof(ProxyMaintStatusStringRoutine), NULL, 
"!UL, !20%D (!%T)<BR>!UL files (!UL/!UL), !UL deleted (!UL/!UL)!AZ",
                   ProxyMaintRoutinePurgeCount,
                   &ProxyMaintScanStartBinTime, &DeltaTime,
                   ProxyMaintFileCount, ProxyMaintUsedBlocks,
                   ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
                   ProxyMaintDeletedUsedBlocks,
                   ProxyMaintDeletedAllocBlocks, cptr);
         {
            /* don't use WASD functions to write into the locked areas */
            $DESCRIPTOR (FaoDsc,
"!AZ, !UL files (!UL/!UL), !UL deleted (!UL/!UL)\0");
            $DESCRIPTOR (StringDsc, ""); 

            StringDsc.dsc$w_length =
               sizeof(ProxyAccountingPtr->StatusString)-1;
            StringDsc.dsc$a_pointer = ProxyAccountingPtr->StatusString;
            InstanceMutexLock (INSTANCE_MUTEX_HTTPD);
            sys$fao (&FaoDsc, NULL, &StringDsc,
                      DigitDayTime(0), ProxyMaintFileCount,
                      ProxyMaintUsedBlocks, ProxyMaintAllocBlocks,
                      ProxyMaintDeletedCount, ProxyMaintDeletedUsedBlocks,
                      ProxyMaintDeletedAllocBlocks);
            InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);
         }

         break;

      case PROXY_MAINT_SCAN_BCKGRND :

         /**********************/
         /* background cleanup */
         /**********************/

         if (WATCH_CATEGORY(WATCH_PROXY_CACHE_MNT))
         {
            WatchThis (NULL, FI_LI, WATCH_PROXY_CACHE_MNT,
"MAINT end BACKGROUND PURGE \
!UL files (!UL/!UL blocks) !UL deleted (!UL/!UL blocks)",
               ProxyMaintFileCount, ProxyMaintUsedBlocks,
               ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
               ProxyMaintDeletedUsedBlocks, ProxyMaintDeletedAllocBlocks);
            ProxyMaintWatchDeviceStats ();
         }

         if (ProxyReportLog)
            WriteFaoStdout (
"%!AZ-I-PROXYMAINT, !20%D, end background purge\n\
!UL files (!UL/!UL), !UL deleted (!UL/!UL)\n",
               Utility, 0,
               ProxyMaintFileCount, ProxyMaintUsedBlocks,
               ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
               ProxyMaintDeletedUsedBlocks,
               ProxyMaintDeletedAllocBlocks);

         if (OpcomMessages & OPCOM_PROXY_MAINT)
            WriteFaoOpcom (
"%!AZ-I-PROXYMAINT, end background purge\r\n\
!UL files (!UL/!UL), !UL deleted (!UL/!UL)",
               Utility,
               ProxyMaintFileCount, ProxyMaintUsedBlocks,
               ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
               ProxyMaintDeletedUsedBlocks,
               ProxyMaintDeletedAllocBlocks);

         ProxyMaintPrintDeviceStats (false);

         if (!ProxyMaintChangeToReactive && !ProxyMaintStopScan)
            WriteFao (ProxyMaintStatusStringBckGrnd,
                      sizeof(ProxyMaintStatusStringBckGrnd), NULL, 
"!UL, !20%D (!%T)<BR>!UL files (!UL/!UL), !UL deleted (!UL/!UL)!AZ",
                      ProxyMaintBackgroundPurgeCount,
                      &ProxyMaintScanStartBinTime, &DeltaTime,
                      ProxyMaintFileCount, ProxyMaintUsedBlocks,
                      ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
                      ProxyMaintDeletedUsedBlocks,
                      ProxyMaintDeletedAllocBlocks, cptr);
         {
            /* don't use WASD functions to write into the locked areas */
            $DESCRIPTOR (FaoDsc,
"!AZ, !UL files (!UL/!UL), !UL deleted (!UL/!UL)\0");
            $DESCRIPTOR (StringDsc, ""); 

            StringDsc.dsc$w_length =
               sizeof(ProxyAccountingPtr->StatusString)-1;
            StringDsc.dsc$a_pointer = ProxyAccountingPtr->StatusString;
            InstanceMutexLock (INSTANCE_MUTEX_HTTPD);
            sys$fao (&FaoDsc, NULL, &StringDsc,
                      DigitDayTime(0), ProxyMaintFileCount,
                      ProxyMaintUsedBlocks, ProxyMaintAllocBlocks,
                      ProxyMaintDeletedCount, ProxyMaintDeletedUsedBlocks,
                      ProxyMaintDeletedAllocBlocks);
            InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);
         }

         break;

      default :
         ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI);
   }

   InstanceMutexLock (INSTANCE_MUTEX_HTTPD);
   ProxyAccountingPtr->PrevFileCount = ProxyMaintFileCount;
   InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);

   /* release the cache lock */
   if (VMSnok (status = InstanceUnLock (INSTANCE_CLUSTER_PROXYMAINT)))
      ErrorExitVmsStatus (status, "InstanceUnLock()", FI_LI);

   if (ProxyMaintScanType == PROXY_MAINT_SCAN_BCKGRND)
   {
      if (ProxyMaintChangeToReactive)
      {
         ProxyMaintChangeToReactive = false;
         ProxyMaintScanType = PROXY_MAINT_SCAN_NONE;
         ProxyMaintScanBegin (PROXY_MAINT_SCAN_REACTIVE);
         return;
      }
   }

   ProxyMaintStopScan = false;
   ProxyMaintScanType = PROXY_MAINT_SCAN_NONE;

   /* if from the command-line wake the main thread */
   if (CliProxyMaint[0]) sys$wake (0, 0);
}

/****************************************************************************/
/*
Initialize the RMS structures ready for the file search.
*/

ProxyMaintScanNow ()

{
   int  status;

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

   if (WATCH_MODULE(WATCH_MOD_PROXY))
      WatchThis (NULL, FI_LI, WATCH_MOD_PROXY,
                 "ProxyMaintScanNow() !&Z", ProxyMaintScanFileSpec);

   ProxyMaintSearchFab = cc$rms_fab;
   ProxyMaintSearchFab.fab$l_fna = ProxyMaintScanFileSpec;
   ProxyMaintSearchFab.fab$b_fns = ProxyMaintScanFileSpecLength;
   ProxyMaintSearchFab.fab$l_nam = &ProxyMaintSearchNam;

   /* initialize the NAM block */
   ProxyMaintSearchNam = cc$rms_nam;
   ProxyMaintSearchNam.nam$l_esa = ProxyMaintExpandedFileName;
   ProxyMaintSearchNam.nam$b_ess = sizeof(ProxyMaintExpandedFileName)-1;
   ProxyMaintSearchNam.nam$l_rsa = ProxyMaintResultFileName;
   ProxyMaintSearchNam.nam$b_rss = sizeof(ProxyMaintResultFileName)-1;

   status = sys$parse (&ProxyMaintSearchFab, 0, 0);
   if (VMSnok (status))
   {
      ProxyMaintScanEnd ();
      return;
   }

   ProxyMaintFileCount = 0;

   /* now we've done the parse do the searching asynchronously */
   ProxyMaintSearchFab.fab$l_fop = FAB$M_ASY;

   ProxyMaintScanSearch ();
}

/****************************************************************************/
/*
Simply queue another asynchronous RMS search.
*/

ProxyMaintScanSearch ()

{
   int  status;
   unsigned long  CurrentTime [2],
                  DeltaTime [2];

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

   if (WATCH_MODULE(WATCH_MOD_PROXY))
      WatchThis (NULL, FI_LI, WATCH_MOD_PROXY, "ProxyMaintScanSearch()");

   if (ProxyMaintStopScan ||
       ProxyMaintChangeToReactive)
   {
      ProxyMaintScanEnd ();
      return;
   }

   switch (ProxyMaintScanType)
   {
      case PROXY_MAINT_SCAN_BCKGRND :

         /* if we should wait before continuing with the next file */
         if (ProxyMaintBackgroundTimer ()) return;

         /* just continue  */
         break;

      case PROXY_MAINT_SCAN_ROUTINE :
      case PROXY_MAINT_SCAN_STATISTICS :

         /* just continue through the complete scan */
         break;

      case PROXY_MAINT_SCAN_REACTIVE :

         /* only recheck free space after every ten files deleted */
         if (ProxyMaintFileCount &&
             (!ProxyMaintDeletedCount || ProxyMaintDeletedCount % 10))
            break;

         if (ProxyMaintDeviceFreeSpace (ProxyMaintTargetPercent))
         {
            /* reached our desired limit, cease purging */
            ProxyMaintScanEnd ();
            return;
         }
         break;

      default :
         ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI);
   }

   if (ProxyMaintFileCount % PROXY_MAINT_SCAN_MONITOR_FILES == 0)
   {
      sys$gettim (&CurrentTime);
      lib$sub_times (&CurrentTime, &ProxyMaintScanStartBinTime, &DeltaTime);

      switch (ProxyMaintScanType)
      {
         case PROXY_MAINT_SCAN_STATISTICS :

            if (WATCH_CATEGORY(WATCH_PROXY_CACHE_MNT))
            {
               WatchThis (NULL, FI_LI, WATCH_PROXY_CACHE_MNT,
                  "MAINT STATISTICS SCAN !UL files (!UL/!UL blocks)",
                  ProxyMaintFileCount, ProxyMaintUsedBlocks,
                  ProxyMaintAllocBlocks);
               ProxyMaintWatchDeviceStats ();
            }

            WriteFao (ProxyMaintStatusStringStatScan,
                      sizeof(ProxyMaintStatusStringStatScan), NULL,
"!UL, !20%D (!%T)<BR>!UL files (!UL/!UL)",
                      ProxyMaintStatScanCount,
                      &ProxyMaintScanStartBinTime, &DeltaTime,
                      ProxyMaintFileCount, ProxyMaintUsedBlocks,
                      ProxyMaintAllocBlocks);
            {
               /* don't use WASD functions to write into the locked areas */
               $DESCRIPTOR (FaoDsc,
"!AZ, STATISTICS, !UL (!UL/!UL)\0");
               $DESCRIPTOR (StringDsc, ""); 

               StringDsc.dsc$w_length =
                  sizeof(ProxyAccountingPtr->StatusString)-1;
               StringDsc.dsc$a_pointer = ProxyAccountingPtr->StatusString;
               InstanceMutexLock (INSTANCE_MUTEX_HTTPD);
               sys$fao (&FaoDsc, NULL, &StringDsc,
                        DigitDayTime(&ProxyMaintScanStartBinTime),
                        ProxyMaintFileCount, ProxyMaintUsedBlocks,
                        ProxyMaintAllocBlocks);
               InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);
            }
            break;

         case PROXY_MAINT_SCAN_REACTIVE :

            if (WATCH_CATEGORY(WATCH_PROXY_CACHE_MNT))
            {
               WatchThis (NULL, FI_LI, WATCH_PROXY_CACHE_MNT,
"MAINT REACTIVE !UL files (!UL/!UL blocks) !UL deleted (!UL/!UL blocks)",
                  ProxyMaintFileCount, ProxyMaintUsedBlocks,
                  ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
                  ProxyMaintDeletedUsedBlocks, ProxyMaintDeletedAllocBlocks);
               ProxyMaintWatchDeviceStats ();
            }

            WriteFao (ProxyMaintStatusStringReactive,
                      sizeof(ProxyMaintStatusStringReactive), NULL,
"!UL, !20%D (!%T), >!UL hours, !UL% max, !UL% target<BR>\
!UL files (!UL/!UL blocks) !UL deleted (!UL/!UL blocks)",
                      ProxyMaintReactivePurgeCount,
                      &ProxyMaintScanStartBinTime, &DeltaTime,
                      ProxyMaintPurgeAtHour, ProxyCacheDeviceMaxPercent,
                      ProxyMaintTargetPercent,
                      ProxyMaintFileCount, ProxyMaintUsedBlocks,
                      ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
                      ProxyMaintDeletedUsedBlocks,
                      ProxyMaintDeletedAllocBlocks);
            {
               /* don't use WASD functions to write into the locked areas */
               $DESCRIPTOR (FaoDsc,
                            "!AZ, REACTIVE, !UL (!UL/!UL) - !UL (!UL/!UL)\0");
               $DESCRIPTOR (StringDsc, ""); 

               StringDsc.dsc$w_length =
                  sizeof(ProxyAccountingPtr->StatusString)-1;
               StringDsc.dsc$a_pointer = ProxyAccountingPtr->StatusString;
               InstanceMutexLock (INSTANCE_MUTEX_HTTPD);
               sys$fao (&FaoDsc, NULL, &StringDsc,
                        DigitDayTime(&ProxyMaintScanStartBinTime),
                        ProxyMaintFileCount, ProxyMaintUsedBlocks,
                        ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
                        ProxyMaintDeletedUsedBlocks,
                        ProxyMaintDeletedAllocBlocks);
               InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);
            }
            break;

         case PROXY_MAINT_SCAN_ROUTINE :

            if (WATCH_CATEGORY(WATCH_PROXY_CACHE_MNT))
            {
               WatchThis (NULL, FI_LI, WATCH_PROXY_CACHE_MNT,
"MAINT ROUTINE !UL files (!UL/!UL blocks) !UL deleted (!UL/!UL blocks)",
                  ProxyMaintFileCount, ProxyMaintUsedBlocks,
                  ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
                  ProxyMaintDeletedUsedBlocks, ProxyMaintDeletedAllocBlocks);
               ProxyMaintWatchDeviceStats ();
            }

            WriteFao (ProxyMaintStatusStringRoutine,
                      sizeof(ProxyMaintStatusStringRoutine), NULL,
"!UL, !20%D (!%T)<BR>\
!UL files (!UL/!UL blocks) !UL deleted (!UL/!UL blocks)",
                      ProxyMaintRoutinePurgeCount,
                      &ProxyMaintScanStartBinTime, &DeltaTime,
                      ProxyMaintFileCount, ProxyMaintUsedBlocks,
                      ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
                      ProxyMaintDeletedUsedBlocks,
                      ProxyMaintDeletedAllocBlocks);
            {
               /* don't use WASD functions to write into the locked areas */
               $DESCRIPTOR (FaoDsc,
"!AZ, ROUTINE, !UL (!UL/!UL) - !UL (!UL/!UL)\0");
               $DESCRIPTOR (StringDsc, ""); 

               StringDsc.dsc$w_length =
                  sizeof(ProxyAccountingPtr->StatusString)-1;
               StringDsc.dsc$a_pointer = ProxyAccountingPtr->StatusString;
               InstanceMutexLock (INSTANCE_MUTEX_HTTPD);
               sys$fao (&FaoDsc, NULL, &StringDsc,
                        DigitDayTime(&ProxyMaintScanStartBinTime),
                        ProxyMaintFileCount, ProxyMaintUsedBlocks,
                        ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
                        ProxyMaintDeletedUsedBlocks,
                        ProxyMaintDeletedAllocBlocks);
               InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);
            }
            break;

         case PROXY_MAINT_SCAN_BCKGRND :

            if (WATCH_CATEGORY(WATCH_PROXY_CACHE_MNT))
            {
               WatchThis (NULL, FI_LI, WATCH_PROXY_CACHE_MNT,
"MAINT BACKGROUND !UL files (!UL/!UL blocks) !UL deleted (!UL/!UL blocks)",
                  ProxyMaintFileCount, ProxyMaintUsedBlocks,
                  ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
                  ProxyMaintDeletedUsedBlocks, ProxyMaintDeletedAllocBlocks);
               ProxyMaintWatchDeviceStats ();
            }

            WriteFao (ProxyMaintStatusStringBckGrnd,
                      sizeof(ProxyMaintStatusStringBckGrnd), NULL,
"!UL, !20%D (!%T)<BR>\
!UL files (!UL/!UL blocks) !UL deleted (!UL/!UL blocks) !UL mSec",
                      ProxyMaintBackgroundPurgeCount,
                      &ProxyMaintScanStartBinTime, &DeltaTime,
                      ProxyMaintFileCount, ProxyMaintUsedBlocks,
                      ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
                      ProxyMaintDeletedUsedBlocks,
                      ProxyMaintDeletedAllocBlocks,
                      ProxyMaintBackgroundInterval);
            {
               /* don't use WASD functions to write into the locked areas */
               $DESCRIPTOR (FaoDsc,
"!AZ, BACKGROUND, !UL (!UL/!UL) - !UL (!UL/!UL)\0");
               $DESCRIPTOR (StringDsc, ""); 

               StringDsc.dsc$w_length =
                  sizeof(ProxyAccountingPtr->StatusString)-1;
               StringDsc.dsc$a_pointer = ProxyAccountingPtr->StatusString;
               InstanceMutexLock (INSTANCE_MUTEX_HTTPD);
               sys$fao (&FaoDsc, NULL, &StringDsc,
                        DigitDayTime(&ProxyMaintScanStartBinTime),
                        ProxyMaintFileCount, ProxyMaintUsedBlocks,
                        ProxyMaintAllocBlocks, ProxyMaintDeletedCount,
                        ProxyMaintDeletedUsedBlocks,
                        ProxyMaintDeletedAllocBlocks);
               InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);
            }
            break;

         default :
            ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI);
      }
   }

   status = sys$search (&ProxyMaintSearchFab, &ProxyMaintScanSearchAst,
                                              &ProxyMaintScanSearchAst);
}

/****************************************************************************/
/*
AST from sys$search().  Check status and report any error.  Otherwise generate
and queue an asynchronous ACP file information QIO.
*/

ProxyMaintScanSearchAst (struct FAB *FabPtr)

{
   static $DESCRIPTOR (DeviceDsc, "");

   int  status;
   ATRDEF  *atptr;
   PROXY_CACHE_FILE_QIO  *cfqptr;

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

   if (WATCH_MODULE(WATCH_MOD_PROXY))
      WatchThis (NULL, FI_LI, WATCH_MOD_PROXY,
                 "ProxyMaintScanSearchAst() !&F !&X !&X",
                 &ProxyMaintScanSearchAst,
                 FabPtr->fab$l_sts, FabPtr->fab$l_stv);

   cfqptr = &ProxyMaintCacheFileQio;

   if (VMSnok (status = FabPtr->fab$l_sts))
   {
      /****************/
      /* search error */
      /****************/

      if (status == RMS$_NMF || (!ProxyMaintFileCount && status == RMS$_FNF))
      {
         /* end of search */
         ProxyMaintScanEnd ();
         return;
      }
      if (status == RMS$_FNF)
      {
         /* file probably deleted during search, just continue searching */
         ProxyMaintScanSearch ();
         return;
      }

      WriteFaoStdout (
"%!AZ-W-PROXYMAINT, !20%D, cache maintenance problem\n\
-!&M\n",
         Utility, 0, status);

      if (OpcomMessages & OPCOM_PROXY_MAINT)
         WriteFaoOpcom (
"%!AZ-W-PROXYMAINT, cache maintenance problem\r\n\
-!&M",
            Utility, status);

      ProxyMaintScanEnd ();
      return;
   }

   ProxyMaintFileCount++;

   ProxyMaintResultFileNameLength = ProxyMaintSearchNam.nam$b_rsl;
   ProxyMaintResultFileName[ProxyMaintResultFileNameLength] = '\0';

   /************/
   /* file ACP */
   /************/

   if (!cfqptr->AcpChannel)
   {
      /* clean the structure out */
      memset (cfqptr, sizeof(ProxyMaintCacheFileQio), 0);

      /* assign a channel to the disk device containing the file */
      DeviceDsc.dsc$a_pointer = ProxyMaintSearchNam.nam$l_dev;
      DeviceDsc.dsc$w_length = ProxyMaintSearchNam.nam$b_dev;

      status = sys$assign (&DeviceDsc, &cfqptr->AcpChannel, 0, 0, 0);
      if (VMSnok (status))
         ErrorExitVmsStatus (status, "sys$assign()", FI_LI);

      /* set up the File Information Block for the ACP interface */
      cfqptr->FibDsc.dsc$w_length = sizeof(struct fibdef);
      cfqptr->FibDsc.dsc$a_pointer = &cfqptr->Fib;

      atptr = &cfqptr->FileAtr;
      atptr->atr$w_size = sizeof(cfqptr->CdtBinTime);
      atptr->atr$w_type = ATR$C_CREDATE;
      atptr->atr$l_addr = &cfqptr->CdtBinTime;
      atptr++;
      atptr->atr$w_size = sizeof(cfqptr->RdtBinTime);
      atptr->atr$w_type = ATR$C_REVDATE;
      atptr->atr$l_addr = &cfqptr->RdtBinTime;
      atptr++;
      atptr->atr$w_size = sizeof(cfqptr->EdtBinTime);
      atptr->atr$w_type = ATR$C_EXPDATE;
      atptr->atr$l_addr = &cfqptr->EdtBinTime;
      atptr++;
      atptr->atr$w_size = sizeof(cfqptr->AscDates);
      atptr->atr$w_type = ATR$C_ASCDATES;
      atptr->atr$l_addr = &cfqptr->AscDates;
      atptr++;
      atptr->atr$w_size = sizeof(cfqptr->RecAttr);
      atptr->atr$w_type = ATR$C_RECATTR;
      atptr->atr$l_addr = &cfqptr->RecAttr;
      atptr++;
      atptr->atr$w_size = atptr->atr$w_type = atptr->atr$l_addr = 0;
   }

   memcpy (&cfqptr->Fib.fib$w_did, &ProxyMaintSearchNam.nam$w_did, 6);

   cfqptr->FileNameDsc.dsc$a_pointer = ProxyMaintSearchNam.nam$l_name;
   cfqptr->FileNameDsc.dsc$w_length = ProxyMaintSearchNam.nam$b_name +
                                      ProxyMaintSearchNam.nam$b_type +
                                      ProxyMaintSearchNam.nam$b_ver;

   status = sys$qio (EfnNoWait, cfqptr->AcpChannel, IO$_ACCESS,
                     &cfqptr->IOsb, &ProxyMaintAcpInfoAst, 0, 
                     &cfqptr->FibDsc, &cfqptr->FileNameDsc, 0, 0,
                     &cfqptr->FileAtr, 0);
}

/****************************************************************************/
/*
AST from ACP QIO. Calculate the relevant file information and determine if it
should be purged (deleted).  If not then just queue another search.  If it
should be then initialize the deletion RMS structures ready for sys$erase().
*/

ProxyMaintAcpInfoAst ()

{
   int  status,
        AccessHours;
   unsigned long  AllocatedVbn,
                  EndOfFileVbn;
   unsigned long  CurrentBinTime[2];
   PROXY_CACHE_FILE_QIO  *cfqptr;

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

   if (WATCH_MODULE(WATCH_MOD_PROXY))
      WatchThis (NULL, FI_LI, WATCH_MOD_PROXY,
                 "ProxyMaintAcpInfoAst() !&F !&X",
                 &ProxyMaintAcpInfoAst, ProxyMaintCacheFileQio.IOsb.Status);

   cfqptr = &ProxyMaintCacheFileQio;

   if (VMSnok (status = cfqptr->IOsb.Status))
   {
      /*************/
      /* ACP error */
      /*************/

      WriteFaoStdout (
         "%!AZ-W-PROXYMAINT, file ACP error (!AZ !UL)\n-!&M\n \\!AZ\\\n",
         Utility, FI_LI, status, ProxyMaintResultFileName);

      /* file probably deleted during search, just continue searching */
      ProxyMaintScanSearch ();
      return;
   }

   /**************/
   /* ACP QIO OK */
   /**************/

   AllocatedVbn = ((cfqptr->RecAttr.fat$l_hiblk & 0xffff) << 16) |
                  ((cfqptr->RecAttr.fat$l_hiblk & 0xffff0000) >> 16);

   EndOfFileVbn = ((cfqptr->RecAttr.fat$l_efblk & 0xffff) << 16) |
                  ((cfqptr->RecAttr.fat$l_efblk & 0xffff0000) >> 16);

   if (WATCH_MODULE(WATCH_MOD_PROXY))
      WatchThis (NULL, FI_LI, WATCH_MOD_PROXY,
                 "AllocatedVbn:!UL EndOfFileVbn:!UL FirstFreeByte:!UL",
                 AllocatedVbn, EndOfFileVbn, cfqptr->RecAttr.fat$w_ffbyte);

   ProxyMaintAllocBlocks += ProxyMaintFileAllocBlocks = AllocatedVbn;
   if (EndOfFileVbn >= 1 && cfqptr->RecAttr.fat$w_ffbyte)
      ProxyMaintUsedBlocks += ProxyMaintFileUsedBlocks = EndOfFileVbn;
   else
      ProxyMaintFileUsedBlocks = 0;

   switch (ProxyMaintScanType)
   {
      case PROXY_MAINT_SCAN_STATISTICS :

         /***************/
         /* search next */
         /***************/

         ProxyMaintScanSearch ();
         return;

      case PROXY_MAINT_SCAN_BCKGRND :
      case PROXY_MAINT_SCAN_REACTIVE :
      case PROXY_MAINT_SCAN_ROUTINE :

         /* check how long ago the file was last accessed */
         sys$gettim (&CurrentBinTime);
         AccessHours = ProxyCacheAgeHours (&CurrentBinTime,
                                           &cfqptr->EdtBinTime);

         if (AccessHours < ProxyMaintPurgeAtHour)
         {
            /***************/
            /* search next */
            /***************/

            ProxyMaintScanSearch ();
            return;
         }

         /* drop through to delete the file */
         break;

      default :
         ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI);
   }

   /***************/
   /* delete file */
   /***************/

   status = sys$qio (EfnNoWait, cfqptr->AcpChannel, IO$_DELETE | IO$M_DELETE,
                     &cfqptr->IOsb, &ProxyMaintDeleteAst, 0, 
                     &cfqptr->FibDsc, &cfqptr->FileNameDsc, 0, 0, 0, 0);
   if (VMSnok (status))
   {
      /* let the AST routine handle it! */
      cfqptr->IOsb.Status = status;
      SysDclAst (&ProxyMaintDeleteAst, 0);
   }
}

/****************************************************************************/
/*
AST from QIO IO$_DELETE.
*/

ProxyMaintDeleteAst ()

{
   int  status;
   PROXY_CACHE_FILE_QIO  *cfqptr;

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

   if (WATCH_MODULE(WATCH_MOD_PROXY))
      WatchThis (NULL, FI_LI, WATCH_MOD_PROXY,
                 "ProxyMaintDeleteAst() !&F !&X", &ProxyMaintDeleteAst);

   cfqptr = &ProxyMaintCacheFileQio;

   if (VMSok (status = cfqptr->IOsb.Status))
   {
      ProxyMaintDeletedCount++;
      ProxyMaintDeletedAllocBlocks += ProxyMaintFileAllocBlocks;
      ProxyMaintDeletedUsedBlocks += ProxyMaintFileUsedBlocks;
   }
   else
   {
      WriteFaoStdout (
         "%!AZ-W-PROXYMAINT, file erase error (!AZ !UL)\n-!&M\n \\!AZ\\\n",
         Utility, FI_LI, status, ProxyMaintEraseFileName);
   }

   /* search next */
   ProxyMaintScanSearch ();
}

/*****************************************************************************/
/*
Return a report and control menu related proxy serving.
*/ 

ProxyMaintReport
(
REQUEST_STRUCT *rqptr,
REQUEST_AST NextTaskFunction
)
{
   static char CacheDeviceFao [] =
"<P><TABLE CELLPADDING=3 CELLSPACING=0 BORDER=1>\n\
<TR><TH>Cache!AZ</TH></TR>\n\
<TR><TD>\n\
<TABLE CELLPADDING=0 CELLSPACING=5 BORDER=0>\n\
<TR><TH ALIGN=right VALIGN=top>Device:</TH><TD>!AZ!&@\n\
<BR>!UL &nbsp;error!%s&nbsp;\n\
<BR>!UL &nbsp;blocks&nbsp; (!ULMB)\n\
<BR>!UL &nbsp;used&nbsp; (!ULMB !UL%)\n\
<BR>!UL &nbsp;free&nbsp; (!ULMB !UL%)\n\
</TD></TR>\n\
<TR><TH ALIGN=right>Free Space:</TH><TD>!AZ</TD></TR>\n\
<TR><TH ALIGN=right>In-Progress:</TH><TD>!AZ</TD></TR>\n\
<TR><TH ALIGN=right VALIGN=top>Statistics Scan:</TH><TD>!AZ</TD></TR>\n\
<TR><TH ALIGN=right VALIGN=top>!AZ:</TH><TD>!AZ</TD></TR>\n\
<TR><TH ALIGN=right VALIGN=top>Reactive Purge:</TH><TD>!AZ</TD></TR>\n\
<TR><TH ALIGN=right VALIGN=top>!AZ:</TH><TD>!AZ</TD></TR>\n\
<TR><TD COLSPAN=2>\n\
<TABLE CELLPADDING=3 CELLSPACING=0 BORDER=0>\n\
<TR><TD>\n\
<FORM METHOD=GET ACTION=\"!AZ\">\n\
<INPUT TYPE=submit VALUE=\" Statistics Scan \">\n\
</FORM>\n\
</TD><TD></TD><TD>\n\
<FORM METHOD=GET ACTION=\"!AZ\">\n\
<INPUT TYPE=submit VALUE=\" Background Purge \">\n\
</FORM>\n\
</TD><TD></TD><TD>\n\
<FORM METHOD=GET ACTION=\"!AZ\">\n\
<INPUT TYPE=submit VALUE=\" Reactive Purge \">\n\
</FORM>\n\
</TD><TD></TD><TD>\n\
<FORM METHOD=GET ACTION=\"!AZ\">\n\
<INPUT TYPE=submit VALUE=\" Routine Purge \">\n\
</FORM>\n\
</TD><TD></TD><TD>\n\
<FORM METHOD=GET ACTION=\"!AZ\">\n\
<INPUT TYPE=submit VALUE=\" Stop \">\n\
</FORM>\n\
</TD></TR>\n\
</TABLE>\n\
</TD></TR>\n\
</TABLE>\n\
</TD></TR>\n\
</TABLE>\n\
</BODY>\n\
</HTML>\n";

   int  status,
        ErrorCount,
        FreeBlocks,
        FreeMBytes,
        FreePercent,
        TotalMBytes,
        TotalBlocks,
        UsedBlocks,
        UsedMBytes,
        UsedPercent;
   unsigned short  Length;
   unsigned long  FaoVector [64];
   unsigned long  *vecptr;
   char  *DevNamePtr;

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

   if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_PROXY))
      WatchThis (rqptr, FI_LI, WATCH_MOD_PROXY,
                 "ProxyMaintReport() !&A", NextTaskFunction);

   rqptr->rqResponse.PreExpired = PRE_EXPIRE_ADMIN;
   AdminPageTitle (rqptr, "Proxy Report");

   status = ProxyMaintStatisticsReport (rqptr, "Statistics");
   if (VMSnok (status))
   {
      rqptr->rqResponse.ErrorTextPtr = "ProxyMaintStatisticsReport()";
      ErrorVmsStatus (rqptr, status, FI_LI);
      SysDclAst (NextTaskFunction, rqptr);
      return;
   }

   status = ProxyMaintDeviceStats (&DevNamePtr, &TotalBlocks,
                                   &UsedBlocks, &FreeBlocks, &ErrorCount);
   if (VMSok (status))
   {
      TotalMBytes = PROXY_MAINT_DEVICE_MBYTES(TotalBlocks);
      UsedMBytes = PROXY_MAINT_DEVICE_MBYTES(UsedBlocks);
      FreeMBytes = PROXY_MAINT_DEVICE_MBYTES(FreeBlocks);
      UsedPercent = PROXY_MAINT_DEVICE_PERCENT_USED(TotalBlocks,FreeBlocks);
      FreePercent = PROXY_MAINT_DEVICE_PERCENT_FREE(TotalBlocks,FreeBlocks);
   }
   else
      TotalBlocks = TotalMBytes = UsedBlocks = UsedMBytes =
         UsedPercent = FreeBlocks = FreeMBytes = FreePercent = 0;

   vecptr = FaoVector;

   if (ProxyCacheEnabled)
      *vecptr++ = "";
   else
      *vecptr++ = "<FONT COLOR=\"#ff0000\"> ... DISABLED</FONT>";

   *vecptr++ = DevNamePtr;
   if (VMSok (status))
      *vecptr++ = "";
   else
   {
      *vecptr++ = " %!&M";
      *vecptr++ = status;
   }
   *vecptr++ = ErrorCount;
   *vecptr++ = TotalBlocks;
   *vecptr++ = TotalMBytes;
   *vecptr++ = UsedBlocks;
   *vecptr++ = UsedMBytes;
   *vecptr++ = UsedPercent;
   *vecptr++ = FreeBlocks;
   *vecptr++ = FreeMBytes;
   *vecptr++ = FreePercent;

   if (ProxyCacheEnabled)
   {
      if (ProxyCacheFreeSpaceAvailable)
         *vecptr++ = "AVAILABLE";
      else
         *vecptr++ = "<FONT COLOR=\"ff0000\"><B>NOT AVAILABLE</B></FONT>";
   }
   else
      *vecptr++ = "<I>n/a</I>";

   switch (ProxyMaintScanType)
   {
      case PROXY_MAINT_SCAN_STATISTICS :
         *vecptr++ = "<FONT COLOR=\"ff0000\"><B>STATISTICS SCAN</B></FONT>";
         break;
      case PROXY_MAINT_SCAN_REACTIVE :
         *vecptr++ = "<FONT COLOR=\"ff0000\"><B>REACTIVE PURGE</B></FONT>";
         break;
      case PROXY_MAINT_SCAN_ROUTINE :
         *vecptr++ = "<FONT COLOR=\"ff0000\"><B>ROUTINE PURGE</B></FONT>";
         break;
      case PROXY_MAINT_SCAN_BCKGRND :
         *vecptr++ = "<FONT COLOR=\"ff0000\"><B>BACKGROUND PURGE</B></FONT>";
         break;
      default :
         if (ProxyCacheEnabled)
            *vecptr++ = "<I>none</I>";
         else
            *vecptr++ = "<I>n/a</I>";
   }

   *vecptr++ = ProxyMaintStatusStringStatScan;
   if (ProxyCacheRoutineHourOfDay == 24)
      *vecptr++ = "<U>Background Purge</U>";
   else
      *vecptr++ = "Background Purge";
   *vecptr++ = ProxyMaintStatusStringBckGrnd;
   *vecptr++ = ProxyMaintStatusStringReactive;
   if (ProxyCacheRoutineHourOfDay >= 0 &&
       ProxyCacheRoutineHourOfDay <= 23)
      *vecptr++ = "<U>Routine Purge</U>";
   else
      *vecptr++ = "Routine Purge";
   *vecptr++ = ProxyMaintStatusStringRoutine;

   *vecptr++ = ADMIN_CONTROL_PROXY_STATISTICS;
   *vecptr++ = ADMIN_CONTROL_PROXY_PURGE_BCKGRND;
   *vecptr++ = ADMIN_CONTROL_PROXY_PURGE_REACTIVE;
   *vecptr++ = ADMIN_CONTROL_PROXY_PURGE_ROUTINE;
   *vecptr++ = ADMIN_CONTROL_PROXY_STOP_SCAN;

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

   SysDclAst (NextTaskFunction, rqptr);
}

/*****************************************************************************/
/*
Return a report on proxy serving.
*/ 

ProxyMaintStatisticsReport
(
REQUEST_STRUCT *rqptr,
char *ReportTitle
)
{
   static char  ResponseFao [] =
"<P><TABLE CELLPADDING=3 CELLSPACING=0 BORDER=1>\n\
<TR><TH>!AZ!AZ</TH></TR>\n\
<TR><TD>\n\
<TABLE CELLPADDING=0 CELLSPACING=3 BORDER=0>\n\
<TR>\n\
\
<TD ALIGN=left VALIGN=top>\n\
<TABLE CELLPADDING=0 CELLSPACING=5 BORDER=0>\n\
<TR><TH ALIGN=right><U>Method</U></TH>\
<TR><TH ALIGN=right>CONNECT:</TH><TD ALIGN=right>!&,UL</TD></TR>\n\
<TR><TH ALIGN=right>DELETE:</TH><TD ALIGN=right>!&,UL</TD></TR>\n\
<TR><TH ALIGN=right>GET:</TH><TD ALIGN=right>!&,UL</TD></TR>\n\
<TR><TH ALIGN=right>HEAD:</TH><TD ALIGN=right>!&,UL</TD></TR>\n\
<TR><TH ALIGN=right>POST:</TH><TD ALIGN=right>!&,UL</TD></TR>\n\
<TR><TH ALIGN=right>PUT:</TH><TD ALIGN=right>!&,UL</TD></TR>\n\
<TR><TH ALIGN=right>Total:</TH><TD ALIGN=right>!&,UL</TD></TR>\n\
</TABLE>\n\
</TD>\
\
<TD>&nbsp;</TD>\n\
\
<TD ALIGN=left VALIGN=top>\n\
<TABLE CELLPADDING=0 CELLSPACING=5 BORDER=0>\n\
<TR><TH ALIGN=right><U>Traffic</U></TH></TR>\n\
<TR><TH ALIGN=right>Network&nbsp;&nbsp;/Rx:</TH><TD ALIGN=right>!&,@SQ</TD></TR>\n\
<TR><TH ALIGN=right>/Tx:</TH><TD ALIGN=right>!&,@SQ</TD></TR>\n\
<TR><TH ALIGN=right></TH><TD ALIGN=right>!UL%</TD></TR>\n\
<TR><TH ALIGN=right>Cache&nbsp;&nbsp;/Rx:</TH><TD ALIGN=right>!&,@SQ</TD></TR>\n\
<TR><TH ALIGN=right>/Tx:</TH><TD ALIGN=right>!&,@SQ</TD></TR>\n\
<TR><TH ALIGN=right></TH><TD ALIGN=right>!UL%</TD></TR>\n\
<TR><TH ALIGN=right>Total:</TH><TD ALIGN=right>!&,@SQ</TD></TR>\n\
</TABLE>\n\
</TD>\
\
<TD>&nbsp;</TD>\n\
\
<TD ALIGN=left VALIGN=top>\n\
<TABLE CELLPADDING=0 CELLSPACING=5 BORDER=0>\n\
<TR><TH ALIGN=right><U>Cache</U></TH>\
<TR><TH ALIGN=right>Read:</TH><TD ALIGN=right>!&,UL/!&,UL</TD><TD>(!UL%)</TD></TR>\n\
<TR><TH ALIGN=right>Write:</TH><TD ALIGN=right>!&,UL</TD><TD>(!UL%)</TD></TR>\n\
<TR><TD>&nbsp;</TD></TR>\n\
<TR><TH ALIGN=right><U>Not&nbsp;Cacheable</U></TH>\
<TR><TH ALIGN=right>Request:</TH><TD ALIGN=right>!&,UL</TD><TD>(!UL%)</TD></TR>\n\
<TR><TH ALIGN=right>Response:</TH><TD ALIGN=right>!&,UL</TD><TD>(!UL%)</TD></TR>\n\
<TR><TH ALIGN=right>Rx/Tx:</TH><TD ALIGN=right>!UL%</TD></TR>\n\
</TABLE>\n\
</TD>\
\
<TD>&nbsp;</TD>\n\
\
<TD ALIGN=left VALIGN=top>\n\
<TABLE CELLPADDING=0 CELLSPACING=5 BORDER=0>\n\
<TR><TH ALIGN=right><U>Network</U></TH></TR>\n\
<TR><TH ALIGN=right>Requested:</TH><TD ALIGN=right>!&,UL</TD><TD>(!UL%)</TD></TR>\n\
<TR><TH ALIGN=right>IPv4:</TH><TD ALIGN=right>!&,UL</TD><TD>(!UL%)</TD></TR>\n\
<TR><TH ALIGN=right>IPv6:</TH><TD ALIGN=right>!&,UL</TD><TD>(!UL%)</TD></TR>\n\
<TR><TD>&nbsp;</TD></TR>\n\
<TR><TH ALIGN=right><U>Host&nbsp;Resolution</U></TH>\
<TR><TH ALIGN=right>Literal:</TH><TD ALIGN=right>!&,UL</TD></TR>\n\
<TR><TH ALIGN=right>DNS:</TH><TD ALIGN=right>!&,UL</TD><TD>(!UL%)</TD></TR>\n\
<TR><TH ALIGN=right>Cache:</TH><TD ALIGN=right>!&,UL</TD></TR>\n\
<TR><TH ALIGN=right>Error:</TH><TD ALIGN=right>!&,UL</TD></TR>\n\
</TABLE>\n\
</TD>\
\
</TR>\n\
</TABLE>\n\
<TABLE CELLPADDING=0 CELLSPACING=3 BORDER=0>\n\
<TR>\n\
\
<TD ALIGN=left VALIGN=top>\n\
<TABLE CELLPADDING=0 CELLSPACING=5 BORDER=0>\n\
<TR><TH ALIGN=right><U>FTP</U></TH></TR>\n\
<TR><TH ALIGN=right>Total:</TH><TD ALIGN=right>!&,UL</TD>\n\
<TH ALIGN=right>DELE:</TH><TD ALIGN=right>!&,UL</TD>\n\
<TH ALIGN=right>DOS:</TH><TD ALIGN=right>!&,UL</TD></TR>\n\
<TR><TH ALIGN=right>Login&nbsp;Fail:</TH><TD ALIGN=right>!&,UL</TD>\n\
<TH ALIGN=right>LIST:</TH><TD ALIGN=right>!&,UL</TD>\n\
<TH ALIGN=right>Unix:</TH><TD ALIGN=right>!&,UL</TD></TR>\n\
<TR><TH></TH><TD></TD>\n\
<TH ALIGN=right>RETR:</TH><TD ALIGN=right>!&,UL</TD>\n\
<TH ALIGN=right>VMS:</TH><TD ALIGN=right>!&,UL</TD></TR>\n\
<TR><TH></TH><TD></TD>\n\
<TH ALIGN=right>STOR:</TH><TD ALIGN=right>!&,UL</TD>\n\
<TH ALIGN=right>?:</TH><TD ALIGN=right>!&,UL</TD></TR>\n\
</TABLE>\n\
</TD>\
\
<TD>&nbsp;&nbsp;</TD>\n\
\
<TD ALIGN=left VALIGN=top>\n\
<TABLE CELLPADDING=0 CELLSPACING=5 BORDER=0>\n\
<TR><TH ALIGN=right><U>Gateway</U></TH></TR>\n\
<TR><TH ALIGN=right>http:&nbsp;&nbsp;-&gt;http:</TH>\
<TD ALIGN=right>!&,UL</TD>\n\
<TH ALIGN=right>&nbsp;https:&nbsp;&nbsp;-&gt;http:</TH>\
<TD ALIGN=right>!&,UL</TD>\n\
<TR><TH ALIGN=right>-&gt;https:</TH><TD ALIGN=right>!&,UL</TD>\n\
<TH ALIGN=right>&nbsp;-&gt;https:</TH><TD ALIGN=right>!&,UL</TD>\n\
<TR><TH ALIGN=right>-&gt;ftp:</TH><TD ALIGN=right>!&,UL</TD>\n\
<TH ALIGN=right>&nbsp;-&gt;ftp:</TH><TD ALIGN=right>!&,UL</TD>\n\
<TR><TH ALIGN=right>IPv4&nbsp;&nbsp;-&gt;Ipv4:</TH>\
<TD ALIGN=right>!&,UL</TD>\n\
<TH ALIGN=right>&nbsp;IPv6&nbsp;&nbsp;-&gt;IPv6:</TH>\
<TD ALIGN=right>!&,UL</TD>\n\
<TR><TH ALIGN=right>-&gt;Ipv6:</TH><TD ALIGN=right>!&,UL</TD>\n\
<TH ALIGN=right>-&gt;IPv4:</TH><TD ALIGN=right>!&,UL</TD>\n\
</TABLE>\n\
</TD>\
\
<TD>&nbsp;&nbsp;</TD>\n\
\
<TD ALIGN=left VALIGN=top>\n\
<TABLE CELLPADDING=0 CELLSPACING=5 BORDER=0>\n\
<TR><TH ALIGN=right><U>Verify</U></TH></TR>\n\
<TR><TH ALIGN=right>Max:</TH><TD ALIGN=right>!UL</TD>\n\
<TH ALIGN=right>Full:</TH><TD ALIGN=right>!UL</TD></TR>\n\
<TR><TH ALIGN=right>Current:</TH><TD ALIGN=right>!UL</TD>\n\
<TH ALIGN=right>200:</TH><TD ALIGN=right>!UL</TD></TR>\n\
<TR><TH ALIGN=right>Set:</TH><TD ALIGN=right>!UL</TD>\n\
<TH ALIGN=right>403:</TH><TD ALIGN=right>!UL</TD></TR>\n\
<TR><TH ALIGN=right>Find:</TH><TD ALIGN=right>!UL</TD>\n\
<TH ALIGN=right>404:</TH><TD ALIGN=right>!UL</TD></TR>\n\
</TABLE>\n\
</TD>\n\
\
</TR>\n\
</TABLE>\n\
</TD></TR>\n\
</TABLE>\n";

   static long  Addx2 = 2;

   int  status,
        ConnectCountNetwork,
        PercentBytesCache,
        PercentBytesNetwork,
        PercentBytesNotCacheable,
        PercentCountCacheRead,
        PercentCountCacheWrite,
        PercentCountRequestNotCacheable,
        PercentCountResponseNotCacheable,
        PercentCountIpv4,
        PercentCountIpv6,
        PercentCountNetwork,
        PercentDnsName,
        TotalCount;
   unsigned short  Length;
   unsigned long  FaoVector [96],
                  QuadBytesCache [2],
                  QuadBytesNotCacheable [2],
                  QuadBytesRaw [2],
                  QuadBytesRawRx [2],
                  QuadBytesRawTx [2],
                  QuadBytesCacheRx [2],
                  QuadBytesCacheTx [2],
                  QuadBytesTotal [2];
   unsigned long  *vecptr;
   float  FloatBytesCache,
          FloatBytesNotCacheable,
          FloatBytesNetwork,
          FloatBytesTotal,
          FloatCountCacheRead,
          FloatCountCacheWrite,
          FloatCountIpv4,
          FloatCountIpv6,
          FloatCountRequestNotCacheable,
          FloatCountResponseNotCacheable,
          FloatCountNetwork,
          FloatCountTotal,
          FloatNetworkCount,
          FloatPercent;
   char  Buffer [4096];
   $DESCRIPTOR (BufferDsc, Buffer);

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

   if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_PROXY))
      WatchThis (rqptr, FI_LI, WATCH_MOD_PROXY,
                 "ProxyMaintStatisticsReport() !AZ", ReportTitle);

   InstanceMutexLock (INSTANCE_MUTEX_HTTPD);

   /*********/
   /* bytes */
   /*********/

   status = lib$addx (&ProxyAccountingPtr->QuadBytesCacheRx,
                      &ProxyAccountingPtr->QuadBytesCacheTx,
                      &QuadBytesCache, &Addx2);

   status = lib$addx (&ProxyAccountingPtr->QuadBytesNotCacheableRx,
                      &ProxyAccountingPtr->QuadBytesNotCacheableTx,
                      &QuadBytesNotCacheable, &Addx2);

   status = lib$addx (&ProxyAccountingPtr->QuadBytesRawRx,
                      &ProxyAccountingPtr->QuadBytesRawTx,
                      &QuadBytesRaw, &Addx2);

   status = lib$addx (&QuadBytesCache,
                      &QuadBytesRaw,
                      &QuadBytesTotal, &Addx2);

   FloatBytesCache = (float)QuadBytesCache[0] +
                     (float)QuadBytesCache[1] * (float)0xffffffff;

   /********************/
   /* bytes percentage */
   /********************/

   FloatBytesNotCacheable = (float)QuadBytesNotCacheable[0] +
                            (float)QuadBytesNotCacheable[1] * (float)0xffffffff;

   FloatBytesNetwork = (float)QuadBytesRaw[0] +
                   (float)QuadBytesRaw[1] * (float)0xffffffff;

   FloatBytesTotal = (float)QuadBytesTotal[0] +
                     (float)QuadBytesTotal[1] * (float)0xffffffff;

   if (FloatBytesTotal > 0.0)
   {
      PercentBytesCache = (int)(FloatPercent =
                          FloatBytesCache * 100.0 / FloatBytesTotal);
      if (FloatPercent - (float)PercentBytesCache >= 0.5) PercentBytesCache++;

      PercentBytesNotCacheable =
         (int)(FloatPercent = FloatBytesNotCacheable * 100.0 / FloatBytesTotal);
      if (FloatPercent - (float)PercentBytesNotCacheable >= 0.5)
         PercentBytesNotCacheable++;

      PercentBytesNetwork =
         (int)(FloatPercent = FloatBytesNetwork * 100.0 / FloatBytesTotal);
      if (FloatPercent - (float)PercentBytesNetwork >= 0.5)
         PercentBytesNetwork++;
   }
   else
      PercentBytesCache = PercentBytesNotCacheable = PercentBytesNetwork = 0;

   /*********************/
   /* counts percentage */
   /*********************/

   FloatCountRequestNotCacheable = ProxyAccountingPtr->RequestNotCacheableCount;
   FloatCountResponseNotCacheable = ProxyAccountingPtr->ResponseNotCacheableCount;
   ConnectCountNetwork = ProxyAccountingPtr->ConnectIpv4Count +
                         ProxyAccountingPtr->ConnectIpv6Count;
   FloatCountIpv4 = (float)ProxyAccountingPtr->ConnectIpv4Count;
   FloatCountIpv6 = (float)ProxyAccountingPtr->ConnectIpv6Count;
   FloatCountNetwork = FloatCountIpv4 + FloatCountIpv6;
   FloatCountCacheRead = ProxyAccountingPtr->CacheReadCount;
   FloatCountCacheWrite = ProxyAccountingPtr->CacheWriteCount;
   FloatCountTotal = FloatCountNetwork + FloatCountCacheRead;

   if (FloatCountTotal > 0.0)
   {
      PercentCountNetwork = 
         (int)(FloatPercent = FloatCountNetwork * 100.0 / FloatCountTotal);
      if (FloatPercent - (float)PercentCountNetwork >= 0.5)
         PercentCountNetwork++;

      PercentCountIpv4 = 
         (int)(FloatPercent = FloatCountIpv4 * 100.0 / FloatCountTotal);
      if (FloatPercent - (float)PercentCountIpv4 >= 0.5)
         PercentCountIpv4++;

      PercentCountIpv6 = 
         (int)(FloatPercent = FloatCountIpv6 * 100.0 / FloatCountTotal);
      if (FloatPercent - (float)PercentCountIpv6 >= 0.5)
         PercentCountIpv6++;

      PercentCountCacheRead = (int)(FloatPercent =
                          FloatCountCacheRead * 100.0 / FloatCountTotal);
      if (FloatPercent - (float)PercentCountCacheRead >= 0.5)
         PercentCountCacheRead++;

      PercentCountRequestNotCacheable =
         (int)(FloatPercent =
               FloatCountRequestNotCacheable * 100.0 / FloatCountTotal);
      if (FloatPercent - (float)PercentCountRequestNotCacheable >= 0.5)
         PercentCountRequestNotCacheable++;
   }
   else
      PercentCountCacheRead = PercentCountRequestNotCacheable =
         PercentCountNetwork = PercentCountIpv4 = PercentCountIpv6 = 0;

   if (FloatCountNetwork > 0.0)
   {
      PercentCountCacheWrite = 
         (int)(FloatPercent = FloatCountCacheWrite * 100.0 / FloatCountNetwork);
      if (FloatPercent - (float)PercentCountCacheWrite >= 0.5)
         PercentCountCacheWrite++;

      PercentCountResponseNotCacheable =
         (int)(FloatPercent =
               FloatCountResponseNotCacheable * 100.0 / FloatCountNetwork);
      if (FloatPercent - (float)PercentCountResponseNotCacheable >= 0.5)
         PercentCountResponseNotCacheable++;
   }
   else
      PercentCountCacheWrite = PercentCountResponseNotCacheable = 0;

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

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

   /***********/
   /* display */
   /***********/

   vecptr = FaoVector;

   *vecptr++ = ReportTitle;

   if (ProxyServingEnabled)
      *vecptr++ = "";
   else
      *vecptr++ = "<FONT COLOR=\"#ff0000\"> ... DISABLED</FONT>";

   *vecptr++ = ProxyAccountingPtr->MethodConnectCount;
   *vecptr++ = ProxyAccountingPtr->MethodDeleteCount;
   *vecptr++ = ProxyAccountingPtr->MethodGetCount;
   *vecptr++ = ProxyAccountingPtr->MethodHeadCount;
   *vecptr++ = ProxyAccountingPtr->MethodPostCount;
   *vecptr++ = ProxyAccountingPtr->MethodPutCount;
   *vecptr++ = ProxyAccountingPtr->MethodGetCount +
               ProxyAccountingPtr->MethodHeadCount +
               ProxyAccountingPtr->MethodPostCount +
               ProxyAccountingPtr->MethodPutCount +
               ProxyAccountingPtr->MethodConnectCount;

   *vecptr++ = &QuadBytesRawRx;
   *vecptr++ = &QuadBytesRawTx;
   *vecptr++ = PercentBytesNetwork;
   *vecptr++ = &QuadBytesCacheRx;
   *vecptr++ = &QuadBytesCacheTx;
   *vecptr++ = PercentBytesCache;
   *vecptr++ = &QuadBytesRaw;

   *vecptr++ = ProxyAccountingPtr->CacheReadCount;
   *vecptr++ = ProxyAccountingPtr->CacheRead304Count;
   *vecptr++ = PercentCountCacheRead;
   *vecptr++ = ProxyAccountingPtr->CacheWriteCount;
   *vecptr++ = PercentCountCacheWrite;
   *vecptr++ = ProxyAccountingPtr->RequestNotCacheableCount;
   *vecptr++ = PercentCountRequestNotCacheable;
   *vecptr++ = ProxyAccountingPtr->ResponseNotCacheableCount;
   *vecptr++ = PercentCountResponseNotCacheable;
   *vecptr++ = PercentBytesNotCacheable;

   *vecptr++ = ConnectCountNetwork;
   *vecptr++ = PercentCountNetwork;
   *vecptr++ = ProxyAccountingPtr->ConnectIpv4Count;
   *vecptr++ = PercentCountIpv4;
   *vecptr++ = ProxyAccountingPtr->ConnectIpv6Count;
   *vecptr++ = PercentCountIpv6;
   *vecptr++ = AccountingPtr->LookupLiteralCount;
   *vecptr++ = AccountingPtr->LookupDnsNameCount;
   *vecptr++ = PercentDnsName;
   *vecptr++ = AccountingPtr->LookupCacheNameCount;
   *vecptr++ = AccountingPtr->LookupDnsNameErrorCount;

   *vecptr++ = ProxyAccountingPtr->FtpCount;
   *vecptr++ = ProxyAccountingPtr->FtpDeleCount;
   *vecptr++ = ProxyAccountingPtr->FtpDosCount;
   *vecptr++ = ProxyAccountingPtr->FtpLoginFailCount;
   *vecptr++ = ProxyAccountingPtr->FtpListCount;
   *vecptr++ = ProxyAccountingPtr->FtpUnixCount;
   *vecptr++ = ProxyAccountingPtr->FtpRetrCount;
   *vecptr++ = ProxyAccountingPtr->FtpVmsCount;
   *vecptr++ = ProxyAccountingPtr->FtpStorCount;
   *vecptr++ = ProxyAccountingPtr->FtpUnknownCount;

   *vecptr++ = ProxyAccountingPtr->GwayHttpHttpCount;
   *vecptr++ = ProxyAccountingPtr->GwayHttpsHttpCount;
   *vecptr++ = ProxyAccountingPtr->GwayHttpHttpsCount;
   *vecptr++ = ProxyAccountingPtr->GwayHttpsHttpsCount;
   *vecptr++ = ProxyAccountingPtr->GwayHttpFtpCount;
   *vecptr++ = ProxyAccountingPtr->GwayHttpsFtpCount;
   *vecptr++ = ProxyAccountingPtr->GwayIpv4Ipv4Count;
   *vecptr++ = ProxyAccountingPtr->GwayIpv6Ipv6Count;
   *vecptr++ = ProxyAccountingPtr->GwayIpv4Ipv6Count;
   *vecptr++ = ProxyAccountingPtr->GwayIpv6Ipv4Count;

   *vecptr++ = ProxyVerifyRecordMax;
   *vecptr++ = ProxyAccountingPtr->VerifyFullCount;
   *vecptr++ = ProxyAccountingPtr->VerifyCurrentCount;
   *vecptr++ = ProxyAccountingPtr->Verify200Count;
   *vecptr++ = ProxyAccountingPtr->VerifySetRecordCount;
   *vecptr++ = ProxyAccountingPtr->Verify403Count;
   *vecptr++ = ProxyAccountingPtr->VerifyFindRecordCount;
   *vecptr++ = ProxyAccountingPtr->Verify404Count;

   InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);

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

   return (status);
}

/*****************************************************************************/
/*
Return a ontrol menu for dynamically adjusting proxy serving.
*/ 

ProxyMaintAdjust
(
REQUEST_STRUCT *rqptr,
REQUEST_AST NextTaskFunction
)
{
   static char  ResponseFao [] =
"<HTML>\n\
<HEAD>\n\
!AZ\
<TITLE>HTTPd !AZ ... Proxy Report/Maintenance</TITLE>\n\
</HEAD>\n\
!AZ\n\
<H2><NOBR>HTTPd !AZ</NOBR></H2>\n\
<H3>Proxy Report/Maintenance</H3>\n\
!20&W\n\
\
<FORM METHOD=POST ACTION=\"!AZ\">\n\
\
<P><TABLE CELLPADDING=3 CELLSPACING=0 BORDER=1>\n\
<TR><TH>Adjust Executing Server</TH></TR>\n\
<TR><TD>\n\
<TABLE CELLPADDING=3 CELLSPACING=0 BORDER=0>\n\
<TR><TH ALIGN=right>Serving:</TH><TD>\n\
<INPUT TYPE=radio NAME=ProxyServingEnabled VALUE=enabled!AZ>enabled\n\
<INPUT TYPE=radio NAME=ProxyServingEnabled VALUE=disabled!AZ>disabled\n\
</TD></TR>\n\
<TR><TH ALIGN=right>Cache:</TH><TD>\n\
<INPUT TYPE=radio NAME=ProxyCacheEnabled VALUE=enabled!AZ>enabled\n\
<INPUT TYPE=radio NAME=ProxyCacheEnabled VALUE=disabled!AZ>disabled\n\
</TD></TR>\n\
<TR><TH ALIGN=right>Unknown Request Fields:</TH><TD>\n\
<INPUT TYPE=radio NAME=ProxyUnknownRequestFields VALUE=enabled!AZ>enabled\n\
<INPUT TYPE=radio NAME=ProxyUnknownRequestFields VALUE=disabled!AZ>disabled\n\
</TD></TR>\n\
<TR><TH ALIGN=right>Add &quot;Forwarded: by&quot;:</TH><TD>\n\
<INPUT TYPE=text SIZE=10 NAME=ProxyForwarded VALUE=\"!AZ\">\n\
</TD></TR>\n\
<TR><TH ALIGN=right>Add &quot;X-Forwarded-For:&quot;:</TH><TD>\n\
<INPUT TYPE=text SIZE=10 NAME=ProxyXForwardedFor VALUE=\"!AZ\">\n\
</TD></TR>\n\
<TR><TH ALIGN=right>Report To Log:</TH><TD>\n\
<INPUT TYPE=radio NAME=ProxyReportLog VALUE=enabled!AZ>enabled\n\
<INPUT TYPE=radio NAME=ProxyReportLog VALUE=disabled!AZ>disabled\n\
</TD></TR>\n\
<TR><TH ALIGN=right>Report Cache To Log:</TH><TD>\n\
<INPUT TYPE=radio NAME=ProxyReportCacheLog VALUE=enabled!AZ>enabled\n\
<INPUT TYPE=radio NAME=ProxyReportCacheLog VALUE=disabled!AZ>disabled\n\
</TD></TR>\n\
<TR><TH ALIGN=right>Host Name Lookup Retry:</TH><TD>\n\
<INPUT TYPE=text SIZE=3 NAME=ProxyHostLookupRetryCount VALUE=!UL>\n\
<FONT SIZE=-1>count</FONT></TD></TR>\n\
<TR><TH ALIGN=right>Maximum Load Size:</TH><TD>\n\
<INPUT TYPE=text SIZE=4 NAME=ProxyCacheFileKBytesMax VALUE=!UL>\n\
<FONT SIZE=-1>kBytes</FONT></TD></TR>\n\
<TR><TH ALIGN=right>No Reload:</TH><TD>\n\
<INPUT TYPE=text SIZE=3 NAME=ProxyCacheNoReloadSeconds VALUE=!UL>\n\
<FONT SIZE=-1>seconds</FONT></TD></TR>\n\
<TR><TH ALIGN=right>Reload List:</TH><TD>\n\
<INPUT TYPE=text SIZE=30 NAME=ProxyCacheReloadList VALUE=\"!AZ\">\n\
<FONT SIZE=-1>hours</FONT></TD></TR>\n\
<TR><TH ALIGN=right>Routine Purge:</TH><TD>\n\
<INPUT TYPE=text SIZE=3 NAME=ProxyCacheRoutineHourOfDay VALUE=!&@>\n\
<FONT SIZE=-1>hour-of-day (00-23)</FONT></TD></TR>\n\
<TR><TH ALIGN=right>Purge List:</TH><TD>\n\
<INPUT TYPE=text SIZE=30 NAME=ProxyCachePurgeList VALUE=\"!AZ\">\n\
<FONT SIZE=-1>hours</FONT></TD></TR>\n\
<TR><TH ALIGN=right>Device Usage Check:</TH><TD>\n\
<INPUT TYPE=text SIZE=2 NAME=ProxyCacheDeviceCheckMinutes VALUE=!UL>\n\
<FONT SIZE=-1>minutes</FONT></TD></TR>\n\
<TR><TH ALIGN=right>Device Usage Max:</TH><TD>\n\
<INPUT TYPE=text SIZE=3 NAME=ProxyCacheDeviceMaxPercent VALUE=!UL>\n\
<FONT SIZE=-1>percent</FONT></TD></TR>\n\
<TR><TH ALIGN=right>Device Reduce By:</TH><TD>\n\
<INPUT TYPE=text SIZE=3 NAME=ProxyCacheDevicePurgePercent VALUE=!UL>\n\
<FONT SIZE=-1>percent</FONT></TD></TR>\n\
<TR><TD COLSPAN=3><BR>\n\
<INPUT TYPE=submit NAME=makechanges VALUE=\" Make Changes \">\n\
<INPUT TYPE=reset VALUE=\" Reset \">\n\
</TD></TR>\n\
</TABLE>\n\
</TD></TR>\n\
</TABLE>\n\
\
</FORM>\n\
\
</BODY>\n\
</HTML>\n";

#define REPBOOL(b)\
if (b)\
{\
   *vecptr++ = RadioButtonChecked;\
   *vecptr++ = RadioButtonUnchecked;\
}\
else\
{\
   *vecptr++ = RadioButtonUnchecked;\
   *vecptr++ = RadioButtonChecked;\
}

   static char  RadioButtonChecked [] = " CHECKED",
                RadioButtonUnchecked [] = "";

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

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

   if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_PROXY))
      WatchThis (rqptr, FI_LI, WATCH_MOD_PROXY,
                 "ProxyMaintAdjustMenu() !&A", NextTaskFunction);

   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++ = ADMIN_CONTROL_PROXY_ADJUST_NOW;

   REPBOOL (ProxyServingEnabled)
   REPBOOL (ProxyCacheEnabled)
   REPBOOL (ProxyUnknownRequestFields)
   if (ProxyForwardedBy == PROXY_FORWARDED_BY)
      *vecptr++ = "BY";
   else
   if (ProxyForwardedBy == PROXY_FORWARDED_FOR)
      *vecptr++ = "FOR";
   else
      *vecptr++ = "disabled";
   if (ProxyXForwardedFor == PROXY_XFORWARDEDFOR_ENABLED)
      *vecptr++ = "ENABLED";
   else
   if (ProxyXForwardedFor == PROXY_XFORWARDEDFOR_ADDRESS)
      *vecptr++ = "ADDRESS";
   else
   if (ProxyXForwardedFor == PROXY_XFORWARDEDFOR_UNKNOWN)
      *vecptr++ = "UNKNOWN";
   else
      *vecptr++ = "disabled";
   REPBOOL (ProxyReportLog)
   REPBOOL (ProxyReportCacheLog)
   *vecptr++ = ProxyHostLookupRetryCount;
   *vecptr++ = ProxyCacheFileKBytesMax;
   *vecptr++ = ProxyCacheNoReloadSeconds;
   *vecptr++ = ProxyCacheReloadListPtr;
   if (ProxyCacheRoutineHourOfDay >= 0)
   {
      *vecptr++ = "!UL";
      *vecptr++ = ProxyCacheRoutineHourOfDay;
   }
   else
      *vecptr++ = "";
   *vecptr++ = ProxyCachePurgeListPtr;
   *vecptr++ = ProxyCacheDeviceCheckMinutes;
   *vecptr++ = ProxyCacheDeviceMaxPercent;
   *vecptr++ = ProxyCacheDevicePurgePercent;

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

   SysDclAst (NextTaskFunction, rqptr);
}

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

ProxyMaintControl
(
REQUEST_STRUCT *rqptr,
REQUEST_AST NextTaskFunction
)
{
   BOOL  MakeChanges;
   int  status;
   unsigned short  Length;
   char  *cptr, *qptr, *sptr, *zptr;
   char  FieldName [128],
         FieldValue [256],
         ProxyCachePurgeList [64],
         ProxyCacheReloadList [64];

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

   if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_PROXY))
      WatchThis (rqptr, FI_LI, WATCH_MOD_PROXY, "ProxyMaintControl()");

   MakeChanges = false;

   /**********************/
   /* parse content body */
   /**********************/

   if (rqptr->rqHeader.Method == HTTP_METHOD_POST)
   {
      if (!rqptr->rqBody.DataPtr)
      {
         /* read all the request body (special case) then AST back */
         rqptr->NextTaskFunction = NextTaskFunction;
         BodyReadBegin (rqptr, &ProxyMaintControl, &BodyProcessReadAll);
         return;
      }
      NextTaskFunction = rqptr->NextTaskFunction;
      qptr = rqptr->rqBody.DataPtr;
   }
   else
      qptr = rqptr->rqHeader.QueryStringPtr;

   while (*qptr)
   {
      status = StringParseQuery (&qptr, 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);
         return;
      }

      /************************/
      /* action button fields */
      /************************/

      if (strsame (FieldName, "hostname", -1))
      {
/** TODO
         ProxyResolveHostCache (NULL, NULL, 0);
**/
         ReportSuccess (rqptr, "Server !AZ host name cache purged.",
                        ServerHostPort);
      }
      else
      if (strsame (FieldName, "makechanges", -1))
         MakeChanges = true;
      else
      if (strsame (FieldName, "ProxyForwarded", -1))
      {
         if (toupper(FieldValue[0]) == 'B')
            ProxyForwardedBy = PROXY_FORWARDED_BY;
         else
         if (toupper(FieldValue[0]) == 'F')
            ProxyForwardedBy = PROXY_FORWARDED_FOR;
         else
            ProxyForwardedBy = PROXY_FORWARDED_DISABLED;
      }
      else
      if (strsame (FieldName, "ProxyXForwardedFor", -1))
      {
          if (toupper(FieldValue[0]) == 'A')
            ProxyXForwardedFor = PROXY_XFORWARDEDFOR_ADDRESS;
          else
          if (toupper(FieldValue[0]) == 'E')
            ProxyXForwardedFor = PROXY_XFORWARDEDFOR_ENABLED;
          else
          if (toupper(FieldValue[0]) == 'U')
            ProxyXForwardedFor = PROXY_XFORWARDEDFOR_UNKNOWN;
          else
            ProxyXForwardedFor = PROXY_XFORWARDEDFOR_DISABLED;
      }
      else
      if (strsame (FieldName, "ProxyServingEnabled", -1))
      {
         InstanceMutexLock (INSTANCE_MUTEX_HTTPD);
         if (toupper(FieldValue[0]) == 'E')
            ProxyServingEnabled = ProxyAccountingPtr->ServingEnabled = true;
         else
         if (toupper(FieldValue[0]) == 'D')
            ProxyServingEnabled = ProxyAccountingPtr->ServingEnabled = false;
         InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);
      }
      else
      if (strsame (FieldName, "ProxyCacheEnabled", -1))
      {
         InstanceMutexLock (INSTANCE_MUTEX_HTTPD);
         if (toupper(FieldValue[0]) == 'E')
            ProxyCacheEnabled = ProxyAccountingPtr->CacheEnabled = true;
         else
         if (toupper(FieldValue[0]) == 'D')
            ProxyCacheEnabled = ProxyAccountingPtr->CacheEnabled = false;
         InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD);
      }
      else
      if (strsame (FieldName, "ProxyHostLookupRetryCount", -1))
      {
         ProxyHostLookupRetryCount = atoi(FieldValue);
         if (ProxyHostLookupRetryCount < 0 || ProxyHostLookupRetryCount > 100)
            ProxyHostLookupRetryCount = 0;
      }
      else
      if (strsame (FieldName, "ProxyCacheFileKBytesMax", -1))
         ProxyCacheFileKBytesMax = atoi(FieldValue);
      else
      if (strsame (FieldName, "ProxyCacheReloadList", -1))
      {
         zptr = (sptr = ProxyCacheReloadList) + sizeof(ProxyCacheReloadList);
         for (cptr = FieldValue; *cptr && sptr < zptr; *sptr++ = *cptr++);
         if (sptr >= zptr)
         {
            ErrorGeneralOverflow (rqptr, FI_LI);
            return;
         }
         *sptr = '\0';
         ProxyCacheReloadListPtr = ProxyCacheReloadList;
      }
      else
      if (strsame (FieldName, "ProxyCacheRoutineHourOfDay", -1))
      {
         if (isdigit(FieldValue[0]))
            ProxyCacheRoutineHourOfDay = atoi(FieldValue);
         else
            ProxyCacheRoutineHourOfDay = -1;
      }
      else
      if (strsame (FieldName, "ProxyCacheDeviceCheckMinutes", -1))
         ProxyCacheDeviceCheckMinutes = atoi(FieldValue);
      else
      if (strsame (FieldName, "ProxyCacheDeviceMaxPercent", -1))
         ProxyCacheDeviceMaxPercent = atoi(FieldValue);
      else
      if (strsame (FieldName, "ProxyCacheDevicePurgePercent", -1))
         ProxyCacheDevicePurgePercent = atoi(FieldValue);
      else
      if (strsame (FieldName, "ProxyCacheNoReloadSeconds", -1))
         ProxyCacheNoReloadSeconds = atoi(FieldValue);
      else
      if (strsame (FieldName, "ProxyCachePurgeList", -1))
      {
         zptr = (sptr = ProxyCachePurgeList) + sizeof(ProxyCachePurgeList);
         for (cptr = FieldValue; *cptr && sptr < zptr; *sptr++ = *cptr++);
         if (sptr >= zptr)
         {
            ErrorGeneralOverflow (rqptr, FI_LI);
            return;
         }
         *sptr = '\0';
         ProxyCachePurgeListPtr = ProxyCachePurgeList;
      }
      else
      if (strsame (FieldName, "ProxyReportLog", -1))
      {
          if (toupper(FieldValue[0]) == 'E')
            ProxyReportLog = true;
         else
         if (toupper(FieldValue[0]) == 'D')
            ProxyReportLog = false;
      }
      else
      if (strsame (FieldName, "ProxyReportCacheLog", -1))
      {
          if (toupper(FieldValue[0]) == 'E')
            ProxyReportCacheLog = true;
         else
         if (toupper(FieldValue[0]) == 'D')
            ProxyReportCacheLog = false;
      }
      else
      if (strsame (FieldName, "ProxyUnknownRequestFields", -1))
      {
         if (toupper(FieldValue[0]) == 'E')
            ProxyUnknownRequestFields = true;
         else
         if (toupper(FieldValue[0]) == 'D')
            ProxyUnknownRequestFields = false;
      }
      else
      {
         /***********/
         /* unknown */
         /***********/

         ErrorGeneral (rqptr, "Unknown query field.", FI_LI);
         return;
      }
   }

   if (MakeChanges)
   {
      ProxyCacheInitValues ();
      ProxyMaintInit ();
      ReportSuccess (rqptr, "Server !AZ new proxy values loaded.",
                     ServerHostPort);
   }

   SysDclAst (NextTaskFunction, rqptr);
}

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