/*****************************************************************************/
/*
                                  WATCH.c

The WATCH facility is a powerful adjunct in server administration.  From the
administration menu it provides an online, real-time, in-browser-window view of
request processing in the running server.  The ability to observe live request
processing on an ad hoc basis, without changing server configuration or
shutting-down/restarting the server process, makes this facility a great
configuration and problem resolution tool.  It allows (amongst other uses)

  o  assessment of mapping rules
  o  assessment of authorization rules
  o  investigation of request processing problems
  o  general observation of server behaviour

A single client per server process can access the WATCH facility at any one
time. The report can be generated for a user-specified number of seconds or
aborted at any time using the browser's stop button.

An event is considered any significant point for which the server code has a
reporting call provided.  These have been selected to provide maximum
information with minimum clutter and impact on server performance.  Obvious
examples are connection acceptance and closure, request path resolution, error
report generation, network reads and writes, etc.  These events may be selected
on a "category" basis from the WATCH menu.

This module also provides functions to display a process using SHOW PROCESS
/ALL via the DCL.C module, and to delete a process, as well as a function to
"peek" at selected fields in the data structures of any request during
processing (intended more as a diagnosis and development tool).

The "module" WATCHing functionality is basically for low-level, on-line
debugging.  It is not intended as a general site administration tool.


EXCLUDING WATCH
---------------
The WATCH menu and associated functionality can be compiled-out of the server
executable using appropriate macros.  The obvious one for elimination is
"module" WATCHing (as mentioned ab ove, generally intended for low-level
debugging).  By default this is not a compile-time inclusion and requires a
WATCH_MOD=1 definition during building.  "Category" WATCHing is intended as a
general site administration tool and so for all but the most demanding sites
should be left compiled in (as it is by default).  To exclude build using a
WATCH_CAT=0 macro.


COMMAND-LINE WATCH
------------------
The command-line version of WATCH accepts a number of variants.

   /WATCH[=NOSTARTUP,][ITEMS=([NO]item[,[NO]item)][,client[,service[,path]]]

The leading keyword NOSTARTUP suppresses WATCH output during server startup.
Items can be any category or module item name (e.g. MAPPING, _MAPURL,
AUTHORIZATION, _AUTH), or ALLCAT or ALLMOD for all categories or modules, and
NOwhatever to turn off a particular WATCH item.  The convenience keyword LIST
provides a list of all category and module keywords that may be used.

   /WATCH=ITEMS=(ALLCAT,NOMAPPING,ALLMOD,NO_FAO)
   /WATCH=NOSTARTUP,ITEMS=(MAPPING,AUTHORIZATION,_CONFIG,_AUTH..)
   /WATCH=ITEMS=(ALLCAT,ALLMOD,NO_FAO,NO_DETAIL),*,*,/HT_ROOT/SRC/*
   /WATCH=LIST


VERSION HISTORY
---------------
16-JUL-2004  MGD  remove potential %DCL-W-TKNOVF in WatchShowSystem()
19-JUL-2003  MGD  revise detached process candidate identification,
                  revise process report format
05-MAY-2003  MGD  remove 'quotas' WATCH item, add (string) 'match'
25-MAR-2003  MGD  WatchShowProcess() username
31-JAN-2003  MGD  DCL and DECnet record building items,
                  additional items in WatchShowSystem(),
                  no menu difference between category and module WATCHing
08-OCT-2002  MGD  add scripting account information
15-JUN-2002  MGD  bugfix; RequestUriPtr formatting in WatchPeek()
04-JUN-2002  MGD  reserve WATCH across all instances
31-MAR-2002  MGD  add CC command-line
03-FEB-2002  MGD  add server process log options
03-JAN-2002  MGD  refine command-line interface
29-OCT-2001  MGD  PERSONA_MACRO reporting
15-SEP-2001  MGD  WatchShowSystem(),
                  per-node and per-cluster instances
04-AUG-2001  MGD  support module WATCHing (WATCH_MOD),
                  conditional compilation of both WATCH_CAT and WATCH_MOD,
                  bugfix; check ParseQueryField() in WatchBegin() for NULL
18-APR-2001  MGD  move TCP/IP agent analysis to NetTcpIpAgentInfo()
14-MAR-2001  MGD  add WatchPeek() throttle, proxy authorization
06-JAN-2001  MGD  DETAIL category
01-OCT-2000  MGD  DCL task changes
26-AUG-2000  MGD  integrate WATCH peek and processing into WatchBegin()
17-JUN-2000  MGD  add "quotas" as a WATCH item,
                  bugfix; WatchCliSettings() storage
28-MAY-2000  MGD  add process quotas, using $getjpi ...lm values from startup
08-MAY-2000  MGD  make path filter a path/track filter
04-MAR-2000  MGD  use NetWriteFaol(), et.al.
02-JAN-2000  MGD  add ODS to WatchPackageInfo()
10-NOV-1999  MGD  use decc$match_wild() for the WatchFilter()
30-OCT-1999  MGD  dignified with a module of it's own (unbundled from ADMIN.C)
07-NOV-1998  MGD  initial (as part of ADMIN.C)
*/
/*****************************************************************************/

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

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

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

#define WASD_MODULE "WATCH"

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

#define WATCH_CATEGORY_LIST_SIZE 1024
#define WATCH_FILTER_SIZE 128

BOOL  WatchCliNoStartup;

char  WatchClientFilter [WATCH_FILTER_SIZE],
      WatchPathFilter [WATCH_FILTER_SIZE],
      WatchServiceFilter [WATCH_FILTER_SIZE];

char  ErrorWatchAuthNeeded [] =
         "Authorization must be enabled to access WATCH!",
      ErrorWatchPersonaNeeded [] =
          "/PERSONA must be enabled to attempt to show this process.",
      ErrorWatchBufferOvf [] = "*ERROR* sys$fao() BUFFEROVF",
      ErrorWatchNumber [] = "Not found in current request list.",
#if !WATCH_CAT
      ErrorWatchNoCategory [] = "Category WATCHing is not a compiled option!",
#endif /* !WATCH_CAT */
#if !WATCH_MOD
      ErrorWatchNoModule [] = "Module WATCHing is not a compiled option!",
#endif /* !WATCH_MOD */
      ErrorWatchSelf [] = "Cannot WATCH yourself!",
      ErrorWatchQueryString [] = "WATCH query string problem (no such item).",
      ErrorWatchSysFao [] = "*ERROR* sys$fao()";

WATCH_STRUCT  Watch,
              WatchCli;

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

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

extern BOOL  DclPersonaServicesAvailable,
             DclScriptDetachProcess,
             LoggingEnabled,
             OdsExtended,
             OperateWithSysPrv,
             PersonaMacro,
             ProtocolHttpsConfigured,
             ProxyCacheEnabled,
             ProxyServingEnabled;

extern int  DclMailboxBytLmRequired,
            EfnWait,
            HttpdTickSecond,
            InstanceNodeCurrent,
            NetAcceptBytLmRequired,
            NetListenBytLmRequired;

extern unsigned long  MailboxMask[],
                      ProcessRightsIdent[],
                      WorldMask[];

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

extern CONFIG_STRUCT  Config;
extern MSG_STRUCT  Msgs;
extern HTTPD_GBLSEC  *HttpdGblSecPtr;
extern HTTPD_PROCESS  HttpdProcess;
extern LIST_HEAD  RequestList;
extern SUPERVISOR_LIST  SupervisorListArray[];
extern SYS_INFO  SysInfo;

/*****************************************************************************/
/*
Check if the watch facility is already in use, report error if it is.  Provide
form for selection of WATCH parameters.
*/ 

WatchReport
(
REQUEST_STRUCT *rqptr,
REQUEST_AST NextTaskFunction
)
{
#if WATCH_CAT

   static char  ResponseFao [] =
"<FORM ACTION=\"!AZ\">\n\
<P><TABLE BORDER=1 CELLSPACING=0 CELLPADDING=5>\n\
<TR><TH>Select WATCH Criteria</TH></TR>\n\
<TR><TD>\n\
<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=5>\n\
<TR><TD VALIGN=top><NOBR>\n\
<B><U>Request</U></B>\n\
<BR><INPUT TYPE=checkbox NAME=rqp VALUE=1 CHECKED> Processing\n\
<BR><INPUT TYPE=checkbox NAME=rqh VALUE=1> Header\n\
<BR><INPUT TYPE=checkbox NAME=rqb VALUE=1> Body\n\
<BR><B><U>Response</U></B><SUP>&nbsp;</SUP>\n\
<BR><INPUT TYPE=checkbox NAME=rsp VALUE=1 CHECKED> Processing\n\
<BR><INPUT TYPE=checkbox NAME=rsh VALUE=1> Header\n\
<BR><INPUT TYPE=checkbox NAME=rsb VALUE=1> Body\n\
</NOBR></TD><TD VALIGN=top><NOBR>\n\
<B><U>General</U></B>\n\
<BR><INPUT TYPE=checkbox NAME=con VALUE=1 CHECKED> Connection\n\
<BR><INPUT TYPE=checkbox NAME=map VALUE=1> Mapping\n\
<BR><INPUT TYPE=checkbox NAME=aut VALUE=1> Authorization\n\
<BR><INPUT TYPE=checkbox NAME=err VALUE=1 CHECKED> Error\n\
<BR><INPUT TYPE=checkbox NAME=cgi VALUE=1> CGI\n\
<BR><INPUT TYPE=checkbox NAME=dcl VALUE=1> DCL\n\
<BR><INPUT TYPE=checkbox NAME=dnt VALUE=1> DECnet\n\
</NOBR></TD><TD VALIGN=top><NOBR>\n\
<B><U>Network</U></B>\n\
<BR><INPUT TYPE=checkbox NAME=net VALUE=1> Activity\n\
<BR><INPUT TYPE=checkbox NAME=oct VALUE=1> Data\n\
<BR><B><U>Other</U></B><SUP>&nbsp;</SUP>\n\
<BR><INPUT TYPE=checkbox NAME=mat VALUE=1> Match\n\
<BR><INPUT TYPE=checkbox NAME=log VALUE=1> !AZLogging!AZ\n\
<BR><INPUT TYPE=checkbox NAME=ssl VALUE=1> !AZSSL!AZ\n\
<BR><INPUT TYPE=checkbox NAME=tmr VALUE=1> Timer\n\
</NOBR></TD><TD VALIGN=top><NOBR>\n\
<B><U>Proxy</U></B>\n\
!AZ\
<BR><INPUT TYPE=checkbox NAME=pxy VALUE=1> Processing\n\
<BR><INPUT TYPE=checkbox NAME=prh VALUE=1> Request Header\n\
<BR><INPUT TYPE=checkbox NAME=prb VALUE=1> Request Body\n\
<BR><INPUT TYPE=checkbox NAME=psh VALUE=1> Response Header\n\
<BR><INPUT TYPE=checkbox NAME=psb VALUE=1> Response Body\n\
!AZ\
!AZ\
<BR><INPUT TYPE=checkbox NAME=pca VALUE=1> Cache\n\
<BR><INPUT TYPE=checkbox NAME=pcm VALUE=1> Cache Maintenance\n\
!AZ\
</NOBR></TD></TR>\n\
!AZ\
<TR><TD COLSPAN=4>\n\
<INPUT TYPE=text NAME=clf SIZE=40 VALUE=\"*\"> Client Filter\n\
<BR><INPUT TYPE=text NAME=sef SIZE=40 VALUE=\"*\"> Service Filter\n\
<BR><INPUT TYPE=text NAME=paf SIZE=40 VALUE=\"*\"> Path/Track Filter\n\
<BR><SELECT NAME=dul>\n\
<OPTION> 30\n\
<OPTION SELECTED> 60\n\
<OPTION> 120\n\
<OPTION> 300\n\
<OPTION> 600\n\
</SELECT> or\n\
<INPUT TYPE=text NAME=dut SIZE=5 VALUE=\"\"> Seconds Duration\n\
<BR><INPUT TYPE=checkbox NAME=stdout VALUE=1> \
Include (<INPUT TYPE=checkbox NAME=only VALUE=1>only) \
in Server Process Log\n\
<P><INPUT TYPE=submit VALUE=\" WATCH \">\n\
<INPUT TYPE=reset VALUE=\" reset \">\n\
</TD></TR>\n\
</TABLE>\n\
</TD></TR>\n\
</TABLE>\n\
</FORM>\n\
\
</BODY>\n\
</HTML>\n";

#if WATCH_MOD

   static char  WatchModFao [] =
"</TABLE>\n\
<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=5>\n\
<TR><TD VALIGN=top><NOBR>\n\
<B><U>Module</U></B>\n\
<BR><INPUT TYPE=checkbox NAME=_adm VALUE=1> ADMIN\n\
<BR><INPUT TYPE=checkbox NAME=_aut VALUE=1> AUTH..\n\
<BR><INPUT TYPE=checkbox NAME=_bod VALUE=1> BODY\n\
<BR><INPUT TYPE=checkbox NAME=_cac VALUE=1> CACHE\n\
<BR><INPUT TYPE=checkbox NAME=_cgi VALUE=1> CGI\n\
<BR><INPUT TYPE=checkbox NAME=_con VALUE=1> CONFIG\n\
</NOBR></TD><TD VALIGN=top><NOBR>\n\
&nbsp;\n\
<BR><INPUT TYPE=checkbox NAME=_dcl VALUE=1> DCL\n\
<BR><INPUT TYPE=checkbox NAME=_dec VALUE=1> DECNET\n\
<BR><INPUT TYPE=checkbox NAME=_dir VALUE=1> DIR\n\
<BR><INPUT TYPE=checkbox NAME=_fao VALUE=1> FAO\n\
<BR><INPUT TYPE=checkbox NAME=_fil VALUE=1> FILE\n\
<BR><INPUT TYPE=checkbox NAME=_hta VALUE=1> HTADMIN\n\
</NOBR></TD><TD VALIGN=top><NOBR>\n\
&nbsp;\n\
</NOBR></TD><TD VALIGN=top><NOBR>\n\
&nbsp;\n\
<BR><INPUT TYPE=checkbox NAME=_ins VALUE=1> INSTANCE\n\
<BR><INPUT TYPE=checkbox NAME=_map VALUE=1> MAPURL\n\
<BR><INPUT TYPE=checkbox NAME=_met VALUE=1> METACON\n\
<BR><INPUT TYPE=checkbox NAME=_msg VALUE=1> MSG\n\
<BR><INPUT TYPE=checkbox NAME=_net VALUE=1> NET\n\
<BR><INPUT TYPE=checkbox NAME=_pro VALUE=1> PROXY..\n\
</NOBR></TD><TD VALIGN=top><NOBR>\n\
&nbsp;\n\
<BR><INPUT TYPE=checkbox NAME=_ods VALUE=1> ODS\n\
<BR><INPUT TYPE=checkbox NAME=_put VALUE=1> PUT\n\
<BR><INPUT TYPE=checkbox NAME=_req VALUE=1> REQUEST\n\
<BR><INPUT TYPE=checkbox NAME=_res VALUE=1> RESPONSE\n\
<BR><INPUT TYPE=checkbox NAME=_ser VALUE=1> SERVICE\n\
<BR><INPUT TYPE=checkbox NAME=_ses VALUE=1> SESOLA..\n\
</NOBR></TD><TD VALIGN=top><NOBR>\n\
&nbsp;\n\
<BR><INPUT TYPE=checkbox NAME=_ssi VALUE=1> SSI\n\
<BR><INPUT TYPE=checkbox NAME=_thr VALUE=1> THROTTLE\n\
<BR><INPUT TYPE=checkbox NAME=_upd VALUE=1> UPD\n\
<BR><INPUT TYPE=checkbox NAME=_vm VALUE=1> VM\n\
<BR><INPUT TYPE=checkbox NAME=_oth VALUE=1> other\n\
<BR><INPUT TYPE=checkbox NAME=_det VALUE=1> detail\n\
</NOBR></TD></TR>\n\
<TR><TD></TD></TR>\n\
</TABLE>\n\
<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=5>\n";

#else

   static char  WatchModFao [] = "";

#endif /* WATCH_MOD */

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

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

   if (Debug) fprintf (stdout, "WatchReport()\n");

   if (Watch.Disabled)
   {
      rqptr->rqResponse.HttpStatus = 403;
      ErrorGeneral (rqptr, MsgFor(rqptr,MSG_GENERAL_DISABLED), FI_LI);
      SysDclAst (NextTaskFunction, rqptr);
      return;
   }

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

   if (WatchInUse (rqptr, false))
   {
      SysDclAst (NextTaskFunction, rqptr);
      return;
   }

   RESPONSE_HEADER_200_HTML (rqptr);
   AdminPageTitle (rqptr, "WATCH Report");

   vecptr = FaoVector;
   *vecptr++ = ADMIN_REPORT_WATCH;
   if (LoggingEnabled)
   {
      *vecptr++ = "";
      *vecptr++ = "";
   }
   else
   {
      *vecptr++ = "<I>";
      *vecptr++ = "</I>";
   }
   if (ProtocolHttpsConfigured)
   {
      *vecptr++ = "";
      *vecptr++ = "";
   }
   else
   {
      *vecptr++ = "<I>";
      *vecptr++ = "</I>";
   }
   if (ProxyServingEnabled)
   {
      *vecptr++ = "";
      *vecptr++ = "";
   }
   else
   {
      *vecptr++ = "<I>\n";
      *vecptr++ = "</I>\n";
   }
   if (ProxyCacheEnabled)
   {
      *vecptr++ = "";
      *vecptr++ = "";
   }
   else
   {
      *vecptr++ = "<I>\n";
      *vecptr++ = "</I>\n";
   }

#if WATCH_MOD
   *vecptr++ = WatchModFao;
#else
   *vecptr++ = "";
#endif

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

   SysDclAst (NextTaskFunction, rqptr);

#else /* WATCH_CAT */

   rqptr->rqResponse.HttpStatus = 403;
   ErrorGeneral (rqptr, ErrorWatchNoCategory, FI_LI);
   SysDclAst (NextTaskFunction, rqptr);

#endif /* WATCH_CAT */
}

/*****************************************************************************/
/*
This function provides both a WATCH-processing report, and a WATCH-peek report.
This can be a WATCH-only report, peek-only, or peek-then-WATCH. If we're going
to use the WATCH-processing report then we have to do it exclusively, check if
the WATCH facility is already in use, report error if it is.  Parse the query
string WATCH parameters.  Report any errors.  Place the parameters into the
WATCH global storage.  This reserves the WATCH facility for this client via the
'Watch.RequestPtr' (and indicate this with a flag in the request structure,
which will be detected as the request concludes and the WATCH facility released
for reuse).  Generate a plain-text HTTP header and output a WATCH report
heading.  If a WATCH-peek report is requested call WatchPeek() to generate it. 
For a peek-only report we declares the next function AST there.  If
WATCH-processing was only/also requested generate a WATCH-processing header
then return BUT do not declare any AST to continue processing.  The client will
just "hang" there receiving output from WatchThis() via the structure pointed
to by 'Watch.RequestPtr'.  Can be used to WATCH all new requests (matching any
filter criteria of course) or in a "one-shot" mode where a single request is
selected to display all categories at any point during it's processing.
*/ 

WatchBegin
(
REQUEST_STRUCT *rqptr,
REQUEST_AST NextTaskFunction
)
{
#if WATCH_CAT

   static char  ResponseFao [] =
"!20%D  WATCH REPORT  !AZ\n\
!#*-\n\
!AZ (!AZ)\n\
!AZ\n\
!AZ\
$ CC (!#AZ/!UL) !AZ\n\
!AZ with !UL CPU!%s and !ULMB running VMS !AZ (!AZ, !AZ, !AZ!AZ)\n\
$ HTTPD !AZ\n\
!AZ\
!&@\
Process: !AZ !AZ !AZ !AZ\n\
!&@\
!&@\
!&@";

   static char  ResponseWatchingFao [] =
"|Time_______|Module__|Line|Item|Category__|Event...|\n";

   static char  DummyReadBuffer [32];

   BOOL  DoPeek,
         DoWatch,
         StdoutOnly,
         StdoutToo;
   int  status,
        ConnectNumber,
        DurationSeconds,
        WatchCategory,
        WatchModule;
   char  *cptr, *qptr, *sptr, *zptr,
         *InstanceClusterPtr,
         *InstanceNodePtr;
   char  Buffer [2048],
         CategoryList [WATCH_CATEGORY_LIST_SIZE],
         ClientFilter [WATCH_FILTER_SIZE],
         FieldName [256],
         FieldValue [WATCH_FILTER_SIZE],
         PathFilter [WATCH_FILTER_SIZE],
         ServiceFilter [WATCH_FILTER_SIZE];
   unsigned short  Length;
   unsigned long  ucnt;
   unsigned long  *vecptr;
   unsigned long  FaoVector [64];
   LIST_ENTRY  *leptr;
   REQUEST_STRUCT  *rqeptr;

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

   if (Debug) fprintf (stdout, "WatchBegin()\n");

   if (Watch.Disabled)
   {
      rqptr->rqResponse.HttpStatus = 403;
      ErrorGeneral (rqptr, MsgFor(rqptr,MSG_GENERAL_DISABLED), FI_LI);
      SysDclAst (NextTaskFunction, rqptr);
      return;
   }

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

   if (WatchInUse (rqptr, true))
   {
      SysDclAst (NextTaskFunction, rqptr);
      return;
   }

   ConnectNumber = DurationSeconds = WatchCategory = WatchModule = 0;
   ClientFilter[0] = PathFilter[0] = ServiceFilter[0] = '\0';
   DoPeek = DoWatch = StdoutOnly = StdoutToo = false;
   if (rqptr->rqHeader.QueryStringLength)
   {
      qptr = rqptr->rqHeader.QueryStringPtr;
      while (*qptr)
      {
         status = StringParseQuery (&qptr, FieldName, sizeof(FieldName),
                                           FieldValue, sizeof(FieldValue));
         if (VMSnok (status))
         {
            if (status == SS$_IVCHAR) rqptr->rqResponse.HttpStatus = 400;
            rqptr->rqResponse.ErrorTextPtr = "parsing query string";
            ErrorVmsStatus (rqptr, status, FI_LI);
            SysDclAst (NextTaskFunction, rqptr);
            return;
         }

         if (strsame (FieldName, "at", -1) && FieldValue[0])
         {
            DoPeek = true;
            ConnectNumber = strtol (FieldValue, NULL, 10);
            if (Debug) fprintf (stdout, "ConnectNumber: %d\n", ConnectNumber);
         }
         else
         if (strsame (FieldName, "this", -1) && FieldValue[0])
         {
            WatchCategory = WATCH_ONE_SHOT_CAT;
#if WATCH_MOD
            WatchModule = WATCH_ONE_SHOT_MOD;
#endif /* WATCH_MOD */
            ConnectNumber = strtol (FieldValue, NULL, 10);
            if (Debug) fprintf (stdout, "ConnectNumber: %d\n", ConnectNumber);
         }
         else
         if (strsame (FieldName, "aut", -1) && FieldValue[0])
            WatchCategory |= WATCH_AUTH;
         else
         if (strsame (FieldName, "cgi", -1) && FieldValue[0])
            WatchCategory |= WATCH_CGI;
         else
         if (strsame (FieldName, "con", -1) && FieldValue[0])
            WatchCategory |= WATCH_CONNECT;
         else
         if (strsame (FieldName, "dcl", -1) && FieldValue[0])
            WatchCategory |= WATCH_DCL;
         else
         if (strsame (FieldName, "dnt", -1) && FieldValue[0])
            WatchCategory |= WATCH_DECNET;
         else
         if (strsame (FieldName, "err", -1) && FieldValue[0])
            WatchCategory |= WATCH_ERROR;
         else
         if (strsame (FieldName, "log", -1) && FieldValue[0])
            WatchCategory |= WATCH_LOG;
         else
         if (strsame (FieldName, "map", -1) && FieldValue[0])
            WatchCategory |= WATCH_MAPPING;
         else
         if (strsame (FieldName, "mat", -1) && FieldValue[0])
            WatchCategory |= WATCH_MATCH;
         else
         if (strsame (FieldName, "net", -1) && FieldValue[0])
            WatchCategory |= WATCH_NETWORK;
         else
         if (strsame (FieldName, "oct", -1) && FieldValue[0])
            WatchCategory |= WATCH_NETWORK_OCTETS;
         else
         if (strsame (FieldName, "pxy", -1) && FieldValue[0])
            WatchCategory |= WATCH_PROXY;
         else
         if (strsame (FieldName, "pca", -1) && FieldValue[0])
            WatchCategory |= WATCH_PROXY_CACHE;
         else
         if (strsame (FieldName, "pcm", -1) && FieldValue[0])
            WatchCategory |= WATCH_PROXY_CACHE_MNT;
         else
         if (strsame (FieldName, "prh", -1) && FieldValue[0])
            WatchCategory |= WATCH_PROXY_REQU_HDR;
         else
         if (strsame (FieldName, "prb", -1) && FieldValue[0])
            WatchCategory |= WATCH_PROXY_REQU_BDY;
         else
         if (strsame (FieldName, "psh", -1) && FieldValue[0])
            WatchCategory |= WATCH_PROXY_RESP_HDR;
         else
         if (strsame (FieldName, "psb", -1) && FieldValue[0])
            WatchCategory |= WATCH_PROXY_RESP_BDY;
         else
         if (strsame (FieldName, "rqp", -1) && FieldValue[0])
            WatchCategory |= WATCH_REQUEST;
         else
         if (strsame (FieldName, "rqb", -1) && FieldValue[0])
            WatchCategory |= WATCH_REQUEST_BODY;
         else
         if (strsame (FieldName, "rqh", -1) && FieldValue[0])
            WatchCategory |= WATCH_REQUEST_HEADER;
         else
         if (strsame (FieldName, "rsp", -1) && FieldValue[0])
            WatchCategory |= WATCH_RESPONSE;
         else
         if (strsame (FieldName, "rsb", -1) && FieldValue[0])
            WatchCategory |= WATCH_RESPONSE_BODY;
         else
         if (strsame (FieldName, "rsh", -1) && FieldValue[0])
            WatchCategory |= WATCH_RESPONSE_HEADER;
         else
         if (strsame (FieldName, "ssl", -1) && FieldValue[0])
            WatchCategory |= WATCH_SESOLA;
         else
         if (strsame (FieldName, "tmr", -1) && FieldValue[0])
            WatchCategory |= WATCH_TIMER;
         else
         if (strsame (FieldName, "clf", -1))
            strcpy (ClientFilter, FieldValue);
         else
         if (strsame (FieldName, "paf", -1))
            strcpy (PathFilter, FieldValue);
         else
         if (strsame (FieldName, "sef", -1))
            strcpy (ServiceFilter, FieldValue);
         else
         if (strsame (FieldName, "stdout", -1))
            StdoutToo = true;
         else
         if (strsame (FieldName, "only", -1))
            StdoutOnly = true;
         else
         if (strsame (FieldName, "dul", -1) ||
             strsame (FieldName, "dut", -1) ||
             strsame (FieldName, "sec", -1) ||
             strsame (FieldName, "seconds", -1))
         {
            for (cptr = FieldValue; *cptr && !isdigit(*cptr); cptr++);
            if (*cptr) DurationSeconds = atoi (cptr);
         }
         else

#if WATCH_MOD

         if (strsame (FieldName, "_adm", -1) && FieldValue[0])
            WatchModule |= WATCH_MOD_ADMIN;
         else
         if (strsame (FieldName, "_aut", -1) && FieldValue[0])
            WatchModule |= WATCH_MOD_AUTH;
         else
         if (strsame (FieldName, "_bod", -1) && FieldValue[0])
            WatchModule |= WATCH_MOD_BODY;
         else
         if (strsame (FieldName, "_cac", -1) && FieldValue[0])
            WatchModule |= WATCH_MOD_CACHE;
         else
         if (strsame (FieldName, "_cgi", -1) && FieldValue[0])
            WatchModule |= WATCH_MOD_CGI;
         else
         if (strsame (FieldName, "_con", -1) && FieldValue[0])
            WatchModule |= WATCH_MOD_CONFIG;
         else
         if (strsame (FieldName, "_dcl", -1) && FieldValue[0])
            WatchModule |= WATCH_MOD_DCL;
         else
         if (strsame (FieldName, "_det", -1) && FieldValue[0])
            WatchModule |= WATCH_MOD__DETAIL;
         else
         if (strsame (FieldName, "_dec", -1) && FieldValue[0])
            WatchModule |= WATCH_MOD_DECNET;
         else
         if (strsame (FieldName, "_dir", -1) && FieldValue[0])
            WatchModule |= WATCH_MOD_DIR;
         else
         if (strsame (FieldName, "_fao", -1) && FieldValue[0])
            WatchModule |= WATCH_MOD_FAO;
         else
         if (strsame (FieldName, "_fil", -1) && FieldValue[0])
            WatchModule |= WATCH_MOD_FILE;
         else
         if (strsame (FieldName, "_hta", -1) && FieldValue[0])
            WatchModule |= WATCH_MOD_HTADMIN;
         else
         if (strsame (FieldName, "_ins", -1) && FieldValue[0])
            WatchModule |= WATCH_MOD_INSTANCE;
         else
         if (strsame (FieldName, "_map", -1) && FieldValue[0])
            WatchModule |= WATCH_MOD_MAPURL;
         else
         if (strsame (FieldName, "_met", -1) && FieldValue[0])
            WatchModule |= WATCH_MOD_METACON;
         else
         if (strsame (FieldName, "_msg", -1) && FieldValue[0])
            WatchModule |= WATCH_MOD_MSG;
         else
         if (strsame (FieldName, "_net", -1) && FieldValue[0])
            WatchModule |= WATCH_MOD_NET;
         else
         if (strsame (FieldName, "_ods", -1) && FieldValue[0])
            WatchModule |= WATCH_MOD_ODS;
         else
         if (strsame (FieldName, "_oth", -1) && FieldValue[0])
            WatchModule |= WATCH_MOD__OTHER;
         else
         if (strsame (FieldName, "_pro", -1) && FieldValue[0])
            WatchModule |= WATCH_MOD_PROXY;
         else
         if (strsame (FieldName, "_put", -1) && FieldValue[0])
            WatchModule |= WATCH_MOD_PUT;
         else
         if (strsame (FieldName, "_req", -1) && FieldValue[0])
            WatchModule |= WATCH_MOD_REQUEST;
         else
         if (strsame (FieldName, "_res", -1) && FieldValue[0])
            WatchModule |= WATCH_MOD_RESPONSE;
         else
         if (strsame (FieldName, "_ser", -1) && FieldValue[0])
            WatchModule |= WATCH_MOD_SERVICE;
         else
         if (strsame (FieldName, "_ses", -1) && FieldValue[0])
            WatchModule |= WATCH_MOD_SESOLA;
         else
         if (strsame (FieldName, "_ssi", -1) && FieldValue[0])
            WatchModule |= WATCH_MOD_SSI;
         else
         if (strsame (FieldName, "_thr", -1) && FieldValue[0])
            WatchModule |= WATCH_MOD_THROTTLE;
         else
         if (strsame (FieldName, "_upd", -1) && FieldValue[0])
            WatchModule |= WATCH_MOD_UPD;
         else
         if (strsame (FieldName, "_vm", -1) && FieldValue[0])
            WatchModule |= WATCH_MOD_VM;
         else

#else

         if (FieldName[0] == '_')
         {
            rqptr->rqResponse.HttpStatus = 403;
            ErrorGeneral (rqptr, ErrorWatchNoModule, FI_LI);
            SysDclAst (NextTaskFunction, rqptr);
            return;
         }
         else

#endif /* WATCH_MOD */

         {
            rqptr->rqResponse.HttpStatus = 403;
            ErrorGeneral (rqptr, ErrorWatchQueryString, FI_LI);
            SysDclAst (NextTaskFunction, rqptr);
            return;
         }
      }
   }
   if (WatchCategory || WatchModule) DoWatch = true;

   if (!DurationSeconds) DurationSeconds = WATCH_ONE_SHOT_DEFAULT_SECONDS;

   if (!DoPeek && !DoWatch)
   {
      rqptr->rqResponse.HttpStatus = 403;
      ErrorGeneral (rqptr, ErrorWatchQueryString, FI_LI);
      SysDclAst (NextTaskFunction, rqptr);
      return;
   }

   if (ConnectNumber)
   {
      /* find this connection number in the current request list */
      for (leptr = RequestList.HeadPtr; leptr; leptr = leptr->NextPtr)
      {
         rqeptr = (REQUEST_STRUCT*)leptr;
         if (Debug)
            fprintf (stdout, "ConnectNumber: %d\n", rqeptr->ConnectNumber);
         if (rqeptr->ConnectNumber == ConnectNumber) break;
      }
      if (!leptr)
      {
         rqptr->rqResponse.HttpStatus = 400;
         ErrorGeneral (rqptr, ErrorWatchNumber, FI_LI);
         SysDclAst (NextTaskFunction, rqptr);
         return;
      }
      if (rqeptr == rqptr)
      {
         rqptr->rqResponse.HttpStatus = 400;
         ErrorGeneral (rqptr, ErrorWatchSelf, FI_LI);
         SysDclAst (NextTaskFunction, rqptr);
         return;
      }
      if (WatchCategory == WATCH_ONE_SHOT_CAT)
      {
         rqeptr->WatchItem = WATCH_ONE_SHOT_ITEM;
         if (rqeptr->DclTaskPtr)
            rqeptr->DclTaskPtr->WatchItem = WATCH_ONE_SHOT_ITEM;
         if (rqeptr->ProxyTaskPtr)
            rqeptr->ProxyTaskPtr->WatchItem = WATCH_ONE_SHOT_ITEM;
      }
   }

   if (!ClientFilter[0]) strcpy (ClientFilter, "*");
   if (!PathFilter[0]) strcpy (PathFilter, "*");
   if (!ServiceFilter[0]) strcpy (ServiceFilter, "*");

   if (WatchCategory == WATCH_ONE_SHOT_CAT)
      strcpy (CategoryList, "ALL");
   else
   {
      zptr = (sptr = CategoryList) + 80;
      /* first the categories */
      for (ucnt = 1; ucnt; ucnt = ucnt << 1)
      {
         cptr = WatchWhat (WatchCategory & ucnt);
         if (cptr)
         {
            if (sptr > CategoryList)
            {
               *sptr++ = ',';
               if (sptr > zptr)
               {
                  zptr = sptr + 80;
                  *sptr++ = '\n';
               }
               else
                  *sptr++ = ' ';
            }
            while (*cptr) *sptr++ = tolower(*cptr++);
         }
      }
      /* then any modules */
      for (ucnt = 1; ucnt; ucnt = ucnt << 1)
      {
         cptr = WatchWhat ((WatchModule & ucnt) | WATCH__MODULE);
         if (cptr)
         {
            if (sptr > CategoryList)
            {
               *sptr++ = ',';
               if (sptr > zptr)
               {
                  zptr = sptr + 80;
                  *sptr++ = '\n';
               }
               else
                  *sptr++ = ' ';
            }
            while (*cptr) *sptr++ = tolower(*cptr++);
         }
      }
      *sptr = '\0';
   }
   if (Debug)
      fprintf (stdout, "%08.08X |%s| %d |%s|%s|%s|\n",
               WatchCategory, CategoryList, DurationSeconds,
               ClientFilter, PathFilter, ServiceFilter);

   /* generate a WATCH report header */
   rqptr->rqResponse.PreExpired = PRE_EXPIRE_WATCH;
   RESPONSE_HEADER_200_PLAIN (rqptr);

   vecptr = FaoVector;
   *vecptr++ = 0;
   *vecptr++ = ServerHostPort;
   *vecptr++ = 36 + strlen(ServerHostPort);
   *vecptr++ = SoftwareID;
   *vecptr++ = BuildDateTime;
   *vecptr++ = TcpIpAgentInfo;
   *vecptr++ = SesolaVersion();
   *vecptr++ = strchr(__VMS_VERSION, ' ') - __VMS_VERSION;
   *vecptr++ = __VMS_VERSION;
   *vecptr++ = __DECC_VER;
   *vecptr++ = WatchFuncCc;
   *vecptr++ = SysInfo.HwName;
   *vecptr++ = SysInfo.AvailCpuCnt;
   *vecptr++ = SysInfo.MemoryMB;
   *vecptr++ = SysInfo.Version;
#ifdef ODS_EXTENDED
   if (OdsExtended)
      *vecptr++ = "ODS-5 enabled";
   else
   if (SysInfo.VersionInteger >= 720)
      *vecptr++ = "ODS-5 disabled";
   else
      *vecptr++ = "ODS-5 unavailable";
#else /* ODS_EXTENDED */
   *vecptr++ = "ODS-5 unavailable";
#endif /* ODS_EXTENDED */
   *vecptr++ = ENAMEL_NAML_USED;
   *vecptr++ = ENAMEL_FIB_USED;
   if (OperateWithSysPrv)
      *vecptr++ = ", SYSPRV";
   else
      *vecptr++ = "";
   *vecptr++ = CommandLine;
   *vecptr++ = WatchServerQuotas();

   if (DclScriptDetachProcess)
   {
      *vecptr++ = "DCL Scripting: detached, !&@PERSONA!AZ !AZ\n";
      if (HttpdScriptAsUserName[0])
      {
         if (CliScriptAs[0])
            *vecptr++ = "/script=as=!AZ, ";
         else
            *vecptr++ = "as !AZ, ";
         *vecptr++ = HttpdScriptAsUserName;
      }
      else
         *vecptr++ = "";
      if (PersonaMacro)
         *vecptr++ = "_MACRO";
      else
         *vecptr++ = "";
      if (DclPersonaServicesAvailable)
         *vecptr++ = "enabled";
      else
         *vecptr++ = "disabled";
   }
   else
   {
      *vecptr++ =
"DCL Scripting: subprocess\n\
BYTLM-available:!UL BYTLM-per-subproc:!&@ (approx !&@ subprocesses) \
BYTLM-net-accept:!UL BYTLM-net-listen:!UL\n";
      *vecptr++ = HttpdProcess.BytLmAvailable;
      if (DclMailboxBytLmRequired)
      {
         *vecptr++ = "!UL";
         *vecptr++ = DclMailboxBytLmRequired;
         *vecptr++ = "!UL";
         *vecptr++ = (HttpdProcess.BytLmAvailable -
                        (NetAcceptBytLmRequired * Config.cfServer.BusyLimit)) /
                     DclMailboxBytLmRequired;
      }
      else
      {
         *vecptr++ = "?";
         *vecptr++ = "?";
      }
      *vecptr++ = NetAcceptBytLmRequired;
      *vecptr++ = NetListenBytLmRequired;
   }

   *vecptr++ = HttpdProcess.PrcNam;
   *vecptr++ = HttpdProcess.ModeName;
   *vecptr++ = HttpdProcess.SysInput;
   *vecptr++ = HttpdProcess.SysOutput;
   InstanceNodePtr = InstanceClusterPtr = NULL;
   if (InstanceNodeCurrent > 1)
   {
      InstanceLockList (INSTANCE_NODE, ", ", &InstanceNodePtr);
      if (InstanceNodePtr)
      {
         *vecptr++ = "Node: !AZ\n";
         *vecptr++ = InstanceNodePtr;
      }
      else
         *vecptr++ = "";
   }
   else
      *vecptr++ = "";
   InstanceLockList (INSTANCE_CLUSTER, ", ", &InstanceClusterPtr);
   if (InstanceClusterPtr)
   {
      *vecptr++ = "Instances: !AZ\n";
      *vecptr++ = InstanceClusterPtr;
   }
   else
      *vecptr++ = "";

   if (DoWatch && !DoPeek)
   {
      *vecptr++ =
"Watching: !AZ (!SL!&@)\n\
Client: \"!AZ\" Service: \"!AZ\" Path: \"!AZ\"\n";
      *vecptr++ = CategoryList;
      *vecptr++ = WatchCategory;
      if (WatchModule)
      {
         *vecptr++ = "/!SL";
         *vecptr++ = WatchModule & ~WATCH__MODULE;
      }
      else
         *vecptr++ = "";
      *vecptr++ = WatchClientFilter;
      *vecptr++ = WatchServiceFilter;
      *vecptr++ = WatchPathFilter;
   }
   else
      *vecptr++ = "";

   status = WriteFaol (Buffer, sizeof(Buffer), &Length, ResponseFao,
                       &FaoVector);
   if (VMSnok (status) || status == SS$_BUFFEROVF)
      ErrorNoticed (status, "WriteFaol()", FI_LI);
   if (StdoutToo || StdoutOnly) fputs (Buffer, stdout);
   NetWrite (rqptr, 0, Buffer, Length);

   if (InstanceNodePtr) VmFree (InstanceNodePtr, FI_LI);
   if (InstanceClusterPtr) VmFree (InstanceClusterPtr, FI_LI);

   if (DoPeek) WatchPeek (rqptr, rqeptr);

   if (!DoWatch)
   {
      SysDclAst (NextTaskFunction, rqptr);
      return;
   }

   /* go on to generate a WATCH processing report */
   if (StdoutToo || StdoutOnly) fputs (ResponseWatchingFao, stdout);
   NetWrite (rqptr, 0, ResponseWatchingFao, sizeof(ResponseWatchingFao)-1);

   /* might as well use the lowest overhead */
   NetReadRaw (rqptr, &WatchDummyReadAst,
               DummyReadBuffer, sizeof(DummyReadBuffer));

   /* now we fill in the other WATCH facility details */
   Watch.Category = WatchCategory;
   Watch.Module = WatchModule & ~WATCH__MODULE;
   Watch.StdoutOnly = StdoutOnly;
   Watch.StdoutToo = StdoutToo;
   Watch.Count = 0;
   strcpy (WatchClientFilter, ClientFilter);
   strcpy (WatchPathFilter, PathFilter);
   strcpy (WatchServiceFilter, ServiceFilter);

   /* make sure we get the duration we asked for! */
   HttpdTimerSet (rqptr, TIMER_OUTPUT, DurationSeconds);
   HttpdTimerSet (rqptr, TIMER_NOPROGRESS, DurationSeconds);

   /* the request now just "hangs", reading WATCH plain-text output! */

#else /* WATCH_CAT */

   rqptr->rqResponse.HttpStatus = 403;
   ErrorGeneral (rqptr, ErrorWatchNoCategory, FI_LI);
   SysDclAst (NextTaskFunction, rqptr);

#endif /* WATCH_CAT */
}

/*****************************************************************************/
/*
Check and report if the WATCH facility is already being used (locally or via
another instance).
*/ 

#if WATCH_CAT

BOOL WatchInUse
(
REQUEST_STRUCT *rqptr,
BOOL ReserveWatch
)
{
   BOOL  InstanceWatchInUse,
         LocalWatchInUse;
   int  status;
   unsigned short  Length;
   unsigned long  *vecptr;
   unsigned long  FaoVector [8];
   char  Buffer [256];

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

   if (Debug) fprintf (stdout, "WatchInUse()\n");

   if (!rqptr)
   {
      /* release WATCH */
      if (VMSnok (status = InstanceUnLock (INSTANCE_NODE_WATCH)))
         ErrorExitVmsStatus (status, "InstanceUnLock()", FI_LI);
      return (false);
   }

   LocalWatchInUse = InstanceWatchInUse = false;
   /* attempt to obtain the WATCH lock */
   status = InstanceLockNoWait (INSTANCE_NODE_WATCH);
   if (status == SS$_NOTQUEUED)
   {
      /* the lock is already in use (WATCH is in use elsewhere) */
      if (Watch.RequestPtr || Watch.Category || Watch.Module)
         LocalWatchInUse = true;
      else
         InstanceWatchInUse = true;
   }
   else
   {
      /* allows for CLI WATCH */
      if (LocalWatchInUse = Watch.Category || Watch.Module)
         if (VMSnok (status = InstanceUnLock (INSTANCE_NODE_WATCH)))
            ErrorExitVmsStatus (status, "InstanceUnLock()", FI_LI);
   }
   if (!(LocalWatchInUse || InstanceWatchInUse))
   {
      if (ReserveWatch)
         Watch.RequestPtr = rqptr;
      else
      if (VMSnok (status = InstanceUnLock (INSTANCE_NODE_WATCH)))
         ErrorExitVmsStatus (status, "InstanceUnLock()", FI_LI);
      return (false);
   }

   vecptr = FaoVector;
   if (InstanceWatchInUse)
      *vecptr++ = "via another instance.";
   else
   if (Watch.RequestPtr)
   {
      *vecptr++ = "by !AZ@!AZ";
      *vecptr++ = Watch.RequestPtr->RemoteUser;
      *vecptr++ = Watch.RequestPtr->rqClient.Lookup.HostName;
   }
   else
      *vecptr++ = "via /WATCH";

   status = WriteFaol (Buffer, sizeof(Buffer), &Length,
                       "WATCH is currently in use !&@", &FaoVector);
   if (VMSnok (status)) ErrorNoticed (status, "WriteFaol()", FI_LI);

   Buffer[Length] = '\0';
   rqptr->rqResponse.HttpStatus = 403;
   ErrorGeneral (rqptr, Buffer, FI_LI);
   return (true);
}                                   

#endif /* WATCH_CAT */

/*****************************************************************************/
/*
Request using the WATCH facility drops the connection.  Release WATCH.
*/ 

WatchEnd (REQUEST_STRUCT *rqptr)

{
#if WATCH_CAT
   static char  BufferFao [] = "|!%T end|\n\0";

   unsigned short  Length;
   char  Buffer [32];

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

   if (Debug) fprintf (stdout, "WatchEnd()\n");

   if (Watch.RequestPtr != rqptr)
      ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI);

   WriteFao (Buffer, sizeof(Buffer), &Length, BufferFao, 0);
   NetWrite (rqptr, 0, Buffer, Length-1);
   if (Watch.StdoutToo || Watch.StdoutOnly) fputs (Buffer, stdout);

   Watch.RequestPtr = NULL;
   Watch.StdoutOnly = Watch.StdoutToo = false;
   Watch.Category = Watch.Count = Watch.Module = 0;
   WatchClientFilter[0] = WatchPathFilter[0] = WatchServiceFilter[0] = '\0';
   WatchInUse (NULL, false);

#endif /* WATCH_CAT */
}                                   

/*****************************************************************************/
/*
The WATCH client concluding the watching can only be detected via a break in
connection which in a quiescent system (no requests being processed) can in
turn only be detected by a broken network read I/O.  This dummy read ASTs to
here where the status can be checked and appropriate action taken.
*/ 

#if WATCH_CAT

WatchDummyReadAst (REQUEST_STRUCT *rqptr)

{
   static char  DummyReadBuffer [32];

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

   if (Debug)
      fprintf (stdout,
         "WatchDummyReadAst() NetReadIOsb.Status %%X%08.08X .Count %d\n",
         rqptr->rqNet.ReadIOsb.Status, rqptr->rqNet.ReadIOsb.Count);

   if (VMSnok (rqptr->rqNet.ReadIOsb.Status))
   {
      RequestEnd (rqptr);
      return;
   }

   /* just in case it wasn't an error! */
   NetReadRaw (rqptr, &WatchDummyReadAst,
               DummyReadBuffer, sizeof(DummyReadBuffer));
}                                   

#endif /* WATCH_CAT */

/*****************************************************************************/
/*
Filter requests WATCHed on the basis of originating client and/or request
contents (service connected, request path).  Return true if the request should
be WATCHed, false if not.
*/ 

BOOL WatchFilter
(
char *ClientHostName,
char *ClientHostAddress,
char *RequestSchemeName,
char *ServerHostPort,
char *PathInfo,
char *TrackId
)
{
#if WATCH_CAT

   char  *cptr, *sptr;

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

   if (Debug)
      fprintf (stdout, "WatchFilter() |%s|%s|%s|\n",
               WatchClientFilter, WatchServiceFilter,
               WatchPathFilter);

   /* this can occur if ASTs are delivered after WATCH use is discontinued */
   if (!Watch.Category && !Watch.Module) return (false);

   if (ClientHostName || ClientHostAddress)
   {
      /* filter on client host name/address */
      if (*(unsigned short*)WatchClientFilter != '*\0')
      {
         if (isdigit(*(sptr = WatchClientFilter)))
            cptr = ClientHostAddress;
         else
            cptr = ClientHostName;
         if (Debug) fprintf (stdout, "cptr |%s|\n", cptr);
         if (!cptr) return (true);
         if (Debug) fprintf (stdout, "cptr |%s| sptr |%s|\n", cptr, sptr);
         if (!decc$match_wild (cptr, sptr)) return (false);
      }
   }

   if (ServerHostPort)
   {
      /* filter on service */
      if (Debug)
         fprintf (stdout, "ServerHostPort |%s|%s|\n",
                  RequestSchemeName, ServerHostPort);
      if (*(unsigned short*)WatchServiceFilter != '*\0')
      {
         sptr = WatchServiceFilter;
         if (!memcmp (sptr, "http:", 5))
         {
            if (strcmp (RequestSchemeName, "http:")) return (false);
            sptr += 5;
            while (*sptr == '/') sptr++;
         }
         else
         if (!memcmp (sptr, "https:", 6))
         {
            if (strcmp (RequestSchemeName, "https:")) return (false);
            sptr += 6;
            while (*sptr == '/') sptr++;
         }
         cptr = ServerHostPort;
         if (Debug) fprintf (stdout, "cptr |%s| sptr |%s|\n", cptr, sptr);
         if (!decc$match_wild (cptr, sptr)) return (false);
      }
   }

   if (PathInfo)
   {
      /* filter on path */
      if (Debug) fprintf (stdout, "PathInfo |%s|\n", PathInfo);
      if (*(unsigned short*)(sptr = WatchPathFilter) != '*\0')
      {
         if (Config.cfTrack.Enabled)
         {
            if (*sptr != '/' && *sptr != '*' && !isalpha(*sptr) && TrackId)
            {
               sptr++;
               cptr = TrackId;
            }
            else
               cptr = PathInfo;
         }
         else
            cptr = PathInfo;
         if (Debug) fprintf (stdout, "cptr |%s| sptr |%s|\n", cptr, sptr);
         if (!decc$match_wild (cptr, sptr)) return (false);
      }
   }

   return (true);

#else /* WATCH_CAT */

   return (false);

#endif /* WATCH_CAT */
}                                   

/*****************************************************************************/
/*
VARIABLE LENGTH ARGUMENT LIST. Function to provide a formatted WATCH entry,
with trailing information from the caller. 'ReportFormat' parameter must be in
a sys$fao() acceptable format and sufficient variable number parameters be
supplied to satisfy any FAO directives in that format string. The report line
is then generated and output to either 'stdout' (the server process log if
/WATCH= qualifier was used) or to the request structure pointed to by the
gloabl storage 'Watch.RequestPtr' (if a request-based WATCH is in use).
*/ 

WatchThis
(
REQUEST_STRUCT *rqptr,
char *SourceModuleName,
int SourceLineNumber,
int Category,
char *ReportFormat,
...
)
{
#if WATCH_CAT

   static char  BufferFao [] = "|!%T !8AZ !4ZL !4ZL !10AZ !&@|\n!AZ";

   int  status, argcnt;
   unsigned short  Length;
   unsigned long  *vecptr;
   unsigned long  FaoVector [128];
   char  *cptr;
   char  Buffer [2048];
   va_list  argptr;
   WATCH_STRUCT  WatchBuffer;

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

   va_count (argcnt);

   if (Debug)
      fprintf (stdout, "WatchThis() %s %d |%s| %d\n",
               SourceModuleName, SourceLineNumber, ReportFormat, argcnt);

   /* this can occur if ASTs are delivered after WATCH use is discontinued */
   if (!Watch.Category && !Watch.Module) return;

   /* can't have module watching active while using the same functions!! */
   memcpy (&WatchBuffer, &Watch, sizeof(WatchBuffer));
   Watch.Category = Watch.Module = 0;

   vecptr = FaoVector;
   *vecptr++ = 0;
   *vecptr++ = SourceModuleName;
   *vecptr++ = SourceLineNumber;
   if (!rqptr)
      *vecptr++ = -1;
   else
   if (!rqptr->WatchItem)
      *vecptr++ = -1;
   else
      *vecptr++ = rqptr->WatchItem % 10000;
   if (!(cptr = WatchWhat (Category))) cptr = "????????";
   *vecptr++ = cptr;

   /* append the report format string and it's parameters */
   *vecptr++ = ReportFormat;
   va_start (argptr, ReportFormat);
   for (argcnt -= 5; argcnt; argcnt--)
      *vecptr++ = va_arg (argptr, unsigned long);
   va_end (argptr);

   if (!(Category & WATCH__MODULE) && Category & WATCH_TIMER)
      *vecptr++ = WatchServerQuotas ();
   else
      *vecptr++ = "";

   status = WriteFaol (Buffer, sizeof(Buffer), &Length, BufferFao, &FaoVector);
   if (VMSnok (status) || status == SS$_BUFFEROVF)
      fprintf (stdout, "%%%s-W-NOTICED2, %s:%d WriteFaol() %%X%08.08X\n",
               Utility, FI_LI, status);
   if (Debug) fprintf (stdout, "|%s|\n", Buffer);

   if (!Watch.RequestPtr || Watch.StdoutToo || Watch.StdoutOnly)
      fputs (Buffer, stdout);
   if (Watch.RequestPtr && (!Watch.StdoutOnly || Category == WATCH_CONNECT))
      NetWrite (Watch.RequestPtr, 0, Buffer, Length);

   /* restore previous values */
   memcpy (&Watch, &WatchBuffer, sizeof(Watch));

#endif /* WATCH_CAT */
}

/*****************************************************************************/
/*
VARIABLE LENGTH ARGUMENT LIST.  Function to provide a formatted data WATCH entry,
with trailing information from the caller.  'DataFormat' parameter must be
in a sys$fao() acceptable format and sufficient variable number parameters be
supplied to satisfy any FAO directives in that format string.  Should include
appropriate carriage-control.
*/ 

WatchDataFormatted
(
char *DataFormat,
...
)
{
#if WATCH_CAT

   int  status,
        argcnt;
   unsigned short  Length;
   unsigned long  *vecptr;
   unsigned long  FaoVector [64];
   char  Buffer [16384];
   va_list  argptr;

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

   va_count (argcnt);

   if (Debug)
      fprintf (stdout, "WatchDataFormatted() |%s| %d\n",
               DataFormat, argcnt);

   /* this can occur if ASTs are delivered after WATCH use is discontinued */
   if (!Watch.Category && !Watch.Module) return;

   vecptr = FaoVector;
   va_start (argptr, DataFormat);
   for (argcnt -= 1; argcnt; argcnt--)
      *vecptr++ = va_arg (argptr, unsigned long);
   va_end (argptr);

   status = WriteFaol (Buffer, sizeof(Buffer), &Length, DataFormat, &FaoVector);
   if (VMSnok (status) || status == SS$_BUFFEROVF)
      ErrorNoticed (status, "WriteFaol()", FI_LI);
   Buffer[Length] = '\0';
   if (Debug) fprintf (stdout, "|%s|\n", Buffer);

   if (!Watch.RequestPtr || Watch.StdoutToo || Watch.StdoutOnly)
      fputs (Buffer, stdout);
   if (Watch.RequestPtr && !Watch.StdoutOnly)
      NetWrite (Watch.RequestPtr, 0, Buffer, Length);

#endif /* WATCH_CAT */
}

/*****************************************************************************/
/*
Output the supplied, non-formatted data in the WATCH report.  Allows any
printable output to be included as a block in the WATCH output.
*/ 

WatchData
(
char *DataPtr,
int DataLength
)
{
#if WATCH_CAT

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

   if (Debug) fprintf (stdout, "WatchData()\n");

   /* this can occur if ASTs are delivered after WATCH use is discontinued */
   if (!Watch.Category && !Watch.Module) return;

   if (!DataPtr) return;

   if (!Watch.RequestPtr || Watch.StdoutToo || Watch.StdoutOnly)
      fputs (DataPtr, stdout);
   if (Watch.RequestPtr && !Watch.StdoutOnly)
      NetWrite (Watch.RequestPtr, 0, DataPtr, DataLength);

   if (DataLength && DataPtr[DataLength-1] == '\n') return;

   if (!Watch.RequestPtr || Watch.StdoutToo || Watch.StdoutOnly)
      fputs ("\n", stdout);
   if (Watch.RequestPtr && !Watch.StdoutOnly)
      NetWrite (Watch.RequestPtr, 0, "\n", 1);

#endif /* WATCH_CAT */
}

/*****************************************************************************/
/*
Ouput the supplied data using WATCH as a hex and printable character dump.
*/ 

WatchDataDump
(
char *DataPtr,
int DataLength
)
{
#if WATCH_CAT

/* 32 bytes by 128 lines comes out to 4096 bytes, the default buffer-full */
#define MAX_LINES 128
#define BYTES_PER_LINE 32
#define BYTES_PER_GROUP 4
#define GROUPS_PER_LINE (BYTES_PER_LINE / BYTES_PER_GROUP)
#define CHARS_PER_LINE ((BYTES_PER_LINE * 3) + GROUPS_PER_LINE + 1)

   static char  HexDigits [] = "0123456789ABCDEF";

   int  ByteCount,
        CurrentDataCount,
        DataCount;
   char  *cptr, *sptr, *zptr,
         *CurrentDataPtr,
         *CurrentDumpPtr;
   char  DumpBuffer [(CHARS_PER_LINE * MAX_LINES)+1];

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

   if (Debug) fprintf (stdout, "WatchDataDump() %d\n", DataLength);

   /* this can occur if ASTs are delivered after WATCH use is discontinued */
   if (!Watch.Category && !Watch.Module) return;

   if (!DataPtr) return;

   zptr = (sptr = DumpBuffer) + sizeof(DumpBuffer)-1;
   cptr = DataPtr;
   DataCount = DataLength;

   while (DataCount)
   {
      CurrentDumpPtr = sptr;
      CurrentDataPtr = cptr;
      CurrentDataCount = DataCount;

      ByteCount = BYTES_PER_LINE;
      while (ByteCount && DataCount)
      {
         *sptr++ = HexDigits[*(unsigned char*)cptr >> 4];
         *sptr++ = HexDigits[*(unsigned char*)cptr & 0xf];
         cptr++;
         DataCount--;
         ByteCount--;
         if (!(ByteCount % BYTES_PER_GROUP)) *sptr++ = ' ';
      }
      while (ByteCount)
      {
         *sptr++ = ' ';
         *sptr++ = ' ';
         ByteCount--;
         if (!(ByteCount % BYTES_PER_GROUP)) *sptr++ = ' ';
      }

      cptr = CurrentDataPtr;
      DataCount = CurrentDataCount;

      ByteCount = BYTES_PER_LINE;
      while (ByteCount && DataCount)
      {
         if (isalnum(*cptr) || ispunct(*cptr) || *cptr == ' ')
            *sptr++ = *cptr++;
         else
         {
            *sptr++ = '.';
            cptr++;
         }
         DataCount--;
         ByteCount--;
      }
      *sptr++ = '\n';

      if (Debug)
      {
         *sptr = '\0';
         /** fprintf (stdout, "|%s|\n", CurrentDumpPtr); **/
      }

      if (!DataCount || !ByteCount)
      {
         *sptr = '\0';
         WatchData (DumpBuffer, sptr - DumpBuffer);
         zptr = (sptr = DumpBuffer) + sizeof(DumpBuffer)-1;
      }
   }

#endif /* WATCH_CAT */
}

/*****************************************************************************/
/*
Return a pointer to a static string containing current server process quotas
compared to what the server originally started with.
*/ 

#if WATCH_CAT

char* WatchServerQuotas ()

{
   static $DESCRIPTOR (QuotasFaoDsc,
"AST:!UL/!UL BIO:!UL/!UL BYT:!UL/!UL DIO:!UL/!UL ENQ:!UL/!UL \
FIL:!UL/!UL PGFL:!UL/!UL PRC:!UL/!UL TQ:!UL/!UL\n\0");
   static char  Buffer [256];
   static $DESCRIPTOR (BufferDsc, Buffer);

   static int  JpiAstCnt,
               JpiBioCnt,
               JpiBytCnt,
               JpiDioCnt,
               JpiEnqCnt,
               JpiFilCnt,
               JpiPagFilCnt,
               JpiPrcCnt,
               JpiTqCnt;

   static struct {
      unsigned short  BufferLength;
      unsigned short  ItemCode;
      unsigned long  BufferAddress;
      unsigned long  ReturnLengthAddress;
   }
      JpiItem [] =
   { { sizeof(JpiAstCnt), JPI$_ASTCNT, &JpiAstCnt, 0 },
     { sizeof(JpiBioCnt), JPI$_BIOCNT, &JpiBioCnt, 0 },
     { sizeof(JpiBytCnt), JPI$_BYTCNT, &JpiBytCnt, 0 },
     { sizeof(JpiDioCnt), JPI$_DIOCNT, &JpiDioCnt, 0 },
     { sizeof(JpiEnqCnt), JPI$_ENQCNT, &JpiEnqCnt, 0 },
     { sizeof(JpiFilCnt), JPI$_FILCNT, &JpiFilCnt, 0 },
     { sizeof(JpiPagFilCnt), JPI$_PAGFILCNT, &JpiPagFilCnt, 0 },
     { sizeof(JpiPrcCnt), JPI$_PRCCNT, &JpiPrcCnt, 0 },
     { sizeof(JpiTqCnt), JPI$_TQCNT, &JpiTqCnt, 0 },
     { 0,0,0,0 }
   };

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

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

   if (Debug) fprintf (stdout, "WatchServerQuotas()\n");

   status = sys$getjpiw (EfnWait, 0, 0, &JpiItem, &IOsb, 0, 0);
   if (VMSok (status)) status = IOsb.Status;
   if (VMSnok (status))
   {
      fprintf (stdout, "%%%s-W-NOTICED2, %s:%d sys$getjpiw() %%X%08.08X\n",
               Utility, FI_LI, status);
      return ("sys$getjpiw() failed!");
   }

   vecptr = &FaoVector;
   *vecptr++ = JpiAstCnt;
   *vecptr++ = HttpdProcess.AstLm;
   *vecptr++ = JpiBioCnt;
   *vecptr++ = HttpdProcess.BioLm;
   *vecptr++ = JpiBytCnt;
   *vecptr++ = HttpdProcess.BytLm;
   *vecptr++ = JpiDioCnt;
   *vecptr++ = HttpdProcess.DioLm;
   *vecptr++ = JpiEnqCnt;
   *vecptr++ = HttpdProcess.EnqLm;
   *vecptr++ = JpiFilCnt;
   *vecptr++ = HttpdProcess.FilLm;
   *vecptr++ = JpiPagFilCnt;
   *vecptr++ = HttpdProcess.PgFlQuo;
   *vecptr++ = JpiPrcCnt;
   *vecptr++ = HttpdProcess.PrcLm;
   *vecptr++ = JpiTqCnt;
   *vecptr++ = HttpdProcess.TqLm;

   status = sys$faol (&QuotasFaoDsc, NULL, &BufferDsc, &FaoVector);
   if (VMSnok (status) || status == SS$_BUFFEROVF)
      fprintf (stdout, "%%%s-W-NOTICED2, %s:%d sys$faol() %%X%08.08X\n",
               Utility, FI_LI, status);

   return (Buffer);
}

#endif /* WATCH_CAT */

/*****************************************************************************/
/*
Return a string corresponding to the function name of the address passed in
'FunctionPtr'.  The '#include watchfunc.h" below provides a static array
containing function address and name details that during module WATCHing can be
used by the FAO.C ('!&F') '!&A' directive to provide function names rather than
just address information.  Returns NULL if the address is unknown.  The
'watchfunc.h' file is generated by the BUILD_WATCHFUNC.COM procedure.
*/ 

char* WatchFunction (void *FunctionPtr)

{
#if WATCH_MOD

   int  idx;

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

   if (Debug) fprintf (stdout, "WatchFunction() 0x%08.08x\n", FunctionPtr);

   if (!FunctionPtr) return (NULL);
   for (idx = 0; WatchFunc[idx].Address; idx++)
      if (WatchFunc[idx].Address == FunctionPtr) break;
   return (WatchFunc[idx].Name);

#else

   return (NULL);

#endif /* WATCH_MOD */
}

/*****************************************************************************/
/*
Return a string corresponding to the bit set in the 'Category' parameter.
*/ 

#if WATCH_CAT

char* WatchWhat (int Category)

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

   if (Debug) fprintf (stdout, "WatchWhat() %d\n", Category);

   if (!(Category & WATCH__MODULE))
   {
      switch (Category)
      {
         case WATCH_AUTH             : return ("AUTHORIZE");
         case WATCH_CONNECT          : return ("CONNECT");
         case WATCH_CGI              : return ("CGI");
         case WATCH_DCL              : return ("DCL");
         case WATCH_DECNET           : return ("DECNET");
         case WATCH_ERROR            : return ("ERROR");
         case WATCH_FILTER           : return ("FILTER");
         case WATCH_LOG              : return ("LOG");
         case WATCH_MAPPING          : return ("MAPPING");
         case WATCH_MATCH            : return ("MATCH");
         case WATCH_NETWORK          : return ("NETWORK");
         case WATCH_NETWORK_OCTETS   : return ("NET-OCTETS");
         case WATCH_NOTICED          : return ("NOTICED");
         case WATCH_PROXY            : return ("PROXY");
         case WATCH_PROXY_CACHE      : return ("PRO-CACHE");
         case WATCH_PROXY_CACHE_MNT  : return ("PRO-CAC-MAINTENANCE");
         case WATCH_PROXY_REQU_HDR   : return ("PRO-REQ-HEADER");
         case WATCH_PROXY_REQU_BDY   : return ("PRO-REQ-BODY");
         case WATCH_PROXY_RESP_HDR   : return ("PRO-RES-HEADER");
         case WATCH_PROXY_RESP_BDY   : return ("PRO-RES-BODY");
         case WATCH_REQUEST          : return ("REQUEST");
         case WATCH_REQUEST_BODY     : return ("REQ-BODY");
         case WATCH_REQUEST_HEADER   : return ("REQ-HEADER");
         case WATCH_RESPONSE         : return ("RESPONSE");
         case WATCH_RESPONSE_BODY    : return ("RES-BODY");
         case WATCH_RESPONSE_HEADER  : return ("RES-HEADER");
         case WATCH_SESOLA           : return ("SSL");
         case WATCH_TIMER            : return ("TIMER");
      }
   }

#if WATCH_MOD

   switch (Category)
   {
      case WATCH_MOD_ADMIN        : return ("_ADMIN");
      case WATCH_MOD_AUTH         : return ("_AUTH..");
      case WATCH_MOD_BODY         : return ("_BODY");
      case WATCH_MOD_CACHE        : return ("_CACHE");
      case WATCH_MOD_CGI          : return ("_CGI");
      case WATCH_MOD_CONFIG       : return ("_CONFIG");
      case WATCH_MOD_DCL          : return ("_DCL");
      case WATCH_MOD_DECNET       : return ("_DECNET");
      case WATCH_MOD_DIR          : return ("_DIR");
      case WATCH_MOD_FAO          : return ("_FAO");
      case WATCH_MOD_FILE         : return ("_FILE");
      case WATCH_MOD_HTADMIN      : return ("_HTADMIN");
      case WATCH_MOD_INSTANCE     : return ("_INSTANCE");
      case WATCH_MOD_MAPURL       : return ("_MAPURL");
      case WATCH_MOD_METACON      : return ("_METACON");
      case WATCH_MOD_MSG          : return ("_MSG");
      case WATCH_MOD_NET          : return ("_NET");
      case WATCH_MOD_REQUEST      : return ("_REQUEST");
      case WATCH_MOD_ODS          : return ("_ODS");
      case WATCH_MOD_PUT          : return ("_PUT");
      case WATCH_MOD_PROXY        : return ("_PROXY..");
      case WATCH_MOD_RESPONSE     : return ("_RESPONSE");
      case WATCH_MOD_SERVICE      : return ("_SERVICE");
      case WATCH_MOD_SESOLA       : return ("_SESOLA..");
      case WATCH_MOD_SSI          : return ("_SSI");
      case WATCH_MOD_THROTTLE     : return ("_THROTTLE");
      case WATCH_MOD_UPD          : return ("_UPD");
      case WATCH_MOD_VM           : return ("_VM");
      /* special cases (no pun intended) */
      case WATCH_MOD__DETAIL      : return ("_detail");
      case WATCH_MOD__OTHER       : return ("_other");
   }

#endif /* WATCH_MOD */

   return (NULL);
}

#endif /* WATCH_CAT */

/*****************************************************************************/
/*
Parse the /WATCH= qualifier string for command-line startup control. General
format is "/WATCH=[NOSTARTUP,]items[,module][,client][,service][,path/track]". 
The first mandatory parameter, 'items', may be preceded by an optional
NOSTARTUP keyword.  This suppresses all WATCH output until the server is ready
to accept requests (reducing the some WATCH item output considerably).  The
'items' parameter can be one or two numbers representing the items to be
displayed (these may be found in the WATCH report output) or more conveniently
can be a parenthesized, comma-separated list of item names.  For example,
"/WATCH=ITEM=(MAPPING,REQUEST,RESPONSE)" and/or module names,
"/WATCH=ITEM=(REQUEST,RESPONSE,_AUTH..,_MAPURL,_METACON)".  The item names can
be any found in WatchWhat() immediately above and must be supplied exactly as
the strings appear in the switch() statement above (i.e. note some have
trailing "..").
*/ 

BOOL WatchCliParse (char *String)

{
#if WATCH_CAT

   BOOL  EndItemList,
         NoItem;
   unsigned long  Category;
   char  *cptr, *sptr, *zptr;

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

   if (Debug) fprintf (stdout, "WatchCliParse() |%s|\n", String);

   if (strsame (String, "/NOWATCH", 6))
   {
      WatchCli.Disabled = -1;
      return (true);
   }
   if (WatchCli.Disabled < 0) return (true);

   cptr = String;
   while (*cptr && *cptr != '=') cptr++;
   while (*cptr == '=' || *cptr == '\"') cptr++;
   if (!*cptr) return (true);
   if (Debug) fprintf (stdout, "cptr |%s|\n", cptr);
   if (*cptr == '(') cptr++;

   if (strsame (cptr, "LIST", -1))
   {
      char  CategoryList [WATCH_CATEGORY_LIST_SIZE];
      sptr = CategoryList;
      for (Category = 1; Category; Category = Category << 1)
      {
         cptr = WatchWhat (Category);
         if (cptr)
         {
            if (sptr > CategoryList) { *sptr++ = ','; *sptr++ = ' '; }
            while (*cptr) *sptr++ = tolower(*cptr++);
         }
      }
#if WATCH_MOD
      /* then any modules */
      for (Category = 1; Category; Category = Category << 1)
      {
         cptr = WatchWhat (Category | WATCH__MODULE);
         if (cptr)
         {
            if (sptr > CategoryList) { *sptr++ = ','; *sptr++ = ' '; }
            while (*cptr) *sptr++ = tolower(*cptr++);
         }
      }
#endif /* WATCH_MOD */
      *sptr = '\0';
      WriteFaoStdout ("%!AZ-I-WATCH, !AZ\n", Utility, CategoryList);
      exit (SS$_NORMAL);
   }

   if (strsame (cptr, "NOSTARTUP", 7))
   {
      WatchCliNoStartup = true;
      while (isalpha(*cptr)) cptr++;
      if (*cptr == ',' || *cptr == '=') cptr++;
   }

   if (strsame (cptr, "ITEM=(", 6) ||
       strsame (cptr, "ITEMS=(", 7))
   {
      cptr += 6;
      if (*cptr == '(') cptr++;
      WatchCli.Category = WatchCli.Module = 0;
      EndItemList = false;
      while (*cptr && *cptr != ')' && !EndItemList)
      {
         sptr = cptr;
         while (*cptr && *cptr != ',' && *cptr != ')') cptr++;
         if (*cptr == ')') EndItemList = true;
         if (*cptr) *cptr++ = '\0';
         if (strsame (sptr, "NO", 2) && !strsame (sptr, "NOTICED", -1))
         {
            NoItem = true;
            sptr += 2;
         }
         else
            NoItem = false;
         if (Debug) fprintf (stdout, "sptr |%s|\n", sptr);
         for (Category = 1; Category; Category = Category << 1)
         {
            zptr = WatchWhat (Category);
            if (zptr && strsame (sptr, zptr, -1))
            {
               if (NoItem)
                  WatchCli.Category &= ~Category;
               else
                  WatchCli.Category |= Category;
               break;
            }
            zptr = WatchWhat (Category | WATCH__MODULE);
            if (zptr && strsame (sptr, zptr, -1))
            {
               if (NoItem)
                  WatchCli.Module &= ~Category;
               else
                  WatchCli.Module |= Category;
               break;
            }
         }
         if (!Category)
         {
            if (strsame (sptr, "ALLCAT", -1))
               WatchCli.Category |= (Category = 0x7fffffff);
            else
            if (strsame (sptr, "ALLMOD", -1))
               WatchCli.Module |= (Category = 0x7fffffff);
         }
         if (!Category)
         {
            WriteFaoStdout ("%!AZ-E-WATCH, unknown item\n \\!AZ\\\n",
                            Utility, sptr);
            return (false);
         }
         if (*cptr == ',') cptr++;
      }
      WatchCli.AllRequests = true;
   }
   else
   {
      if (!(WatchCli.Category = atoi(cptr)))
      {
         WriteFaoStdout ("%!AZ-E-WATCH, invalid category number", Utility);
         return (false);
      }
      while (*cptr && (*cptr == '-' || isdigit(*cptr))) cptr++;
      if (*cptr == ',') cptr++;
      if (*cptr == '-' || isdigit(*cptr))
      {
         if (!(WatchCli.Module = atoi(cptr)))
         {
            WriteFaoStdout ("%!AZ-E-WATCH, invalid module number", Utility);
            return (false);
         }
         while (*cptr && (*cptr == '-' || isdigit(*cptr))) cptr++;
         if (*cptr == ',') cptr++;
      }
      WatchCli.AllRequests = true;
   }

   if (Debug) fprintf (stdout, "cptr |%s|\n", cptr);
   if (*cptr == ')') return (true);
   zptr = (sptr = WatchClientFilter) + sizeof(WatchClientFilter);
   while (*cptr && *cptr != ',' && *cptr != '\"' && sptr < zptr)
      *sptr++ = *cptr++;
   if (sptr >= zptr)
   {
      WriteFaoStdout ("%!AZ-E-WATCH, invalid client filter", Utility);
      return (false);
   }
   *sptr = '\0';
   if (*cptr == ',') cptr++;

   if (Debug) fprintf (stdout, "cptr |%s|\n", cptr);
   if (*cptr == ')') return (true);
   zptr = (sptr = WatchServiceFilter) + sizeof(WatchServiceFilter);
   while (*cptr && *cptr != ',' && *cptr != '\"' && sptr < zptr)
      *sptr++ = *cptr++;
   if (sptr >= zptr)
   {
      WriteFaoStdout ("%!AZ-E-WATCH, invalid service filter", Utility);
      return (false);
   }
   *sptr = '\0';
   if (*cptr == ',') cptr++;

   if (Debug) fprintf (stdout, "cptr |%s|\n", cptr);
   if (*cptr == ')') return (true);
   zptr = (sptr = WatchPathFilter) + sizeof(WatchPathFilter);
   while (*cptr && *cptr != ',' && *cptr != '\"' && sptr < zptr)
      *sptr++ = *cptr++;
   if (sptr >= zptr)
   {
      WriteFaoStdout ("%!AZ-E-WATCH, invalid path/track filter", Utility);
      return (false);
   }
   *sptr = '\0';

   if (!WatchClientFilter[0]) strcpy (WatchClientFilter, "*");
   if (!WatchPathFilter[0]) strcpy (WatchPathFilter, "*");
   if (!WatchServiceFilter[0]) strcpy (WatchServiceFilter, "*");

   return (true);

#else /* WATCH_CAT */

   WriteFaoStdout ("%!AZ-E-WATCH, is not a compiled option", Utility);
   return (false);

#endif /* WATCH_CAT */
}

/*****************************************************************************/
/*
Just print out the current WATCH settings.
*/ 

WatchCliSettings ()

{
#if WATCH_CAT

   unsigned long  Category;
   char  *cptr, *sptr;
   char  CategoryList [WATCH_CATEGORY_LIST_SIZE];

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

   if (Debug) fprintf (stdout, "WatchCliSettings()\n");

   if (WatchCli.Disabled)
   {
      WriteFaoStdout ("%!AZ-I-WATCH, disabled\n", Utility);
      return;
   }

   if (!WatchCli.Category && !WatchCli.Module) return;

   sptr = CategoryList;
   /* first any categories */
   for (Category = 1; Category; Category = Category << 1)
   {
      cptr = WatchWhat (WatchCli.Category & Category);
      if (cptr)
      {
         if (sptr > CategoryList) { *sptr++ = ','; *sptr++ = ' '; }
         while (*cptr) *sptr++ = tolower(*cptr++);
      }
   }
   /* then any modules */
   for (Category = 1; Category; Category = Category << 1)
   {
      cptr = WatchWhat ((WatchCli.Module & Category) | WATCH__MODULE);
      if (cptr)
      {
         if (sptr > CategoryList) { *sptr++ = ','; *sptr++ = ' '; }
         while (*cptr) *sptr++ = tolower(*cptr++);
      }
   }
   *sptr = '\0';

   WriteFaoStdout (
"%!AZ-I-WATCH, !&?NOSTARTUP \r\r(!SL,!SL) !AZ\n\
-!AZ-I-WATCH, client filter \"!AZ\"\n\
-!AZ-I-WATCH, service filter \"!AZ\"\n\
-!AZ-I-WATCH, path/track filter \"!AZ\"\n",
            Utility, WatchCliNoStartup,
            WatchCli.Category, WatchCli.Module, CategoryList,
            Utility, WatchClientFilter,
            Utility, WatchServiceFilter,
            Utility, WatchPathFilter);

#endif /* WATCH_CAT */
}

/*****************************************************************************/
/*
Generate a report page listing all of the processes belonging to the server
process.
*/

/* seems a lot but I recall some site having a HUGE number of IDs */
#define JPI_PROCESS_RIGHTS_MAX 1024

#define PSCAN$_GETJPI_BUFFER_SIZE 24

WatchProcessReport
(
REQUEST_STRUCT *rqptr,
REQUEST_AST NextTaskFunction
)
{
   static char  BeginPage [] =
"<P><TABLE CELLSPACING=0 CELLPADDING=1 BORDER=0>\n\
<TR><TH></TH>\
<TH ALIGN=left><U>PID</U>&nbsp;&nbsp;</TH>\
<TH ALIGN=left><U>User</U>&nbsp;&nbsp;</TH>\
<TH ALIGN=left><U>Process Name</U>&nbsp;&nbsp;</TH>\
<TH ALIGN=left><U>Image</U>&nbsp;&nbsp;</TH>\
<TH ALIGN=left><U>Mode</U>&nbsp;&nbsp;</TH>\
<TH ALIGN=left><U>State</U>&nbsp;&nbsp;</TH>\
<TH ALIGN=left><U>Priority</U></TH>\
</TR>\n\
<TR HEIGHT=5></TR>\n";

   /* the final column just adds a little white-space on the page far right */
   static char  ProcessFao [] =
"<TR>\
<TD ALIGN=right><A HREF=\"!AZ?pid=!8XL&puser=!AZ\">!3ZL</A>&nbsp;&nbsp;</TD>\
<TD!AZ>!8XL</A>&nbsp;&nbsp;</TD>\
<TD!AZ>!AZ&nbsp;&nbsp;</TD>\
<TD!AZ>!AZ&nbsp;&nbsp;</TD>\
<TD!AZ>!AZ&nbsp;&nbsp;</TD>\
<TD!AZ>!AZ&nbsp;&nbsp;</TD>\
<TD!AZ>!AZ&nbsp;&nbsp;</TD>\
<TD ALIGN=right!AZ>!UL&nbsp;/&nbsp;!UL</TD>\
<TH>&nbsp;&nbsp;</TH>\
</TR>\n";

   static char  EndPageFao [] =
"</TABLE>\n\
<HR SIZE=1 NOSHADE WIDTH=60% ALIGN=left>\n\
</BODY>\n\
</HTML>\n";

   static char  *StateNameArray [] = {
                "1234","COLPG","MWAIT","CEF","PFW","LEF","LEFO",
                "HIB","HIBO","SUSP","SUSPO","FPG","COM","COMO","CUR" };

   static unsigned long  GetJpiControlFlags = JPI$M_IGNORE_TARGET_STATUS;

   static unsigned long  JpiMode,
                         JpiPid,
                         JpiPri,
                         JpiPrib,
                         JpiRightsSize,
                         JpiState;
   static char  JpiImagName [256],
                JpiNodeName [32],
                JpiPrcNam [16],
                JpiUserName [13];

   static struct
   {
      unsigned short  buf_len;
      unsigned short  item;
      unsigned char   *buf_addr;
      unsigned short  *short_ret_len;
   }
      JpiItems [] =
   {
      { sizeof(GetJpiControlFlags), JPI$_GETJPI_CONTROL_FLAGS,
        &GetJpiControlFlags, 0 },
      { sizeof(JpiPid), JPI$_PID, &JpiPid, 0 },
      { sizeof(JpiPri), JPI$_PRI, &JpiPri, 0 },
      { sizeof(JpiPrib), JPI$_PRIB, &JpiPrib, 0 },
      { sizeof(JpiMode), JPI$_MODE, &JpiMode, 0 },
      { sizeof(JpiImagName), JPI$_IMAGNAME, &JpiImagName, 0 },
      { sizeof(JpiPrcNam), JPI$_PRCNAM, &JpiPrcNam, 0 },
      { sizeof(JpiUserName), JPI$_USERNAME, &JpiUserName, 0 },
      { sizeof(JpiState), JPI$_STATE, &JpiState, 0 },
      { sizeof(JpiRightsSize), JPI$_RIGHTS_SIZE, &JpiRightsSize, 0 },
#define JPI_PROCESS_RIGHTS_ITEM 10
      { 0, JPI$_PROCESS_RIGHTS, 0, 0 },
      { 0,0,0,0 }
   },
      ScanItems [] =
   {
      { 0, PSCAN$_GETJPI_BUFFER_SIZE, 2048, 0},
      { 0,0,0,0 }
   };

   int  idx, status,
        IdentCount,
        ProcessCount,
        SetPrvStatus;
   unsigned long  *vecptr;
   unsigned long  ProcessContext;
   unsigned long  FaoVector [32];
   char  *cptr, *sptr,
         *StateNamePtr;
   unsigned long  JpiProcessRights [JPI_PROCESS_RIGHTS_MAX*2];
   IO_SB  IOsb;

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

   if (WATCH_MODULE(WATCH_MOD__OTHER))
      WatchThis (NULL, FI_LI, WATCH_MOD__OTHER, "WatchProcessReport()");

   JpiItems[JPI_PROCESS_RIGHTS_ITEM].buf_len = sizeof(JpiProcessRights);
   JpiItems[JPI_PROCESS_RIGHTS_ITEM].buf_addr = &JpiProcessRights;

   ProcessContext = 0;
   status = sys$process_scan (&ProcessContext, &ScanItems);
   if (Debug) fprintf (stdout, "sys$process_scan() %%X%08.08X\n", status);
   if (VMSnok (status))
   {
      rqptr->rqResponse.ErrorTextPtr = "sys$process_scan()";
      ErrorVmsStatus (rqptr, status, FI_LI);
      SysDclAst (NextTaskFunction, rqptr);
      return;
   }

   rqptr->rqResponse.PreExpired = PRE_EXPIRE_ADMIN;
   RESPONSE_HEADER_200_HTML (rqptr);
   AdminPageTitle (rqptr, "Process Report", BeginPage);

   /* detached scripts (possibly executing as a non-server username) */
   if (DclScriptDetachProcess)
      if (VMSnok (SetPrvStatus = sys$setprv (1, &MailboxMask, 0, 0)))
         ErrorExitVmsStatus (SetPrvStatus, "sys$setprv()", FI_LI);

   ProcessCount = 0;

   for (;;)
   {
      status = sys$getjpiw (EfnWait, &ProcessContext, 0,
                            &JpiItems, &IOsb, 0, 0);
      if (VMSok (status)) status = IOsb.Status;
      if (VMSnok (status)) break;

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

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

      if (WATCH_MODULE(WATCH_MOD__OTHER))
         WatchThis (NULL, FI_LI, WATCH_MOD__OTHER, "!8XL !&Z !&Z !UL",
                    JpiPid, JpiUserName, JpiPrcNam, JpiRightsSize);

      if (DclScriptDetachProcess &&
          JpiPid != HttpdProcess.Pid)
      {
         if (JpiRightsSize > sizeof(JpiProcessRights))
         {
            char  Buffer [32];
            sprintf (Buffer, "sys$getjpiw() %08.08X", JpiPid);
            ErrorNoticed (SS$_BUFFEROVF, Buffer, FI_LI);
         }

         /* look through each of the identifiers in the list */
         idx = 0;
         for (IdentCount = JpiRightsSize / 8;
              IdentCount && JpiProcessRights[idx] != ProcessRightsIdent[0];
              IdentCount--) idx += 2;

         /* if we didn't find the identifier then continue */
         if (!IdentCount) continue;
      }

      ProcessCount++;
      if (ProcessCount % 2)
         sptr = "";
      else
         sptr = " BGCOLOR=\"#eeeeee\"";

      for (cptr = JpiImagName; *cptr && *cptr != ';'; cptr++);
      if (*cptr == ';') *cptr-- = '\0';
      while (cptr > JpiImagName && *cptr != ']') cptr--;
      if (*cptr == ']') cptr++;

      if (JpiState > 0 && JpiState <= 14)
         StateNamePtr = StateNameArray[JpiState];
      else
         sprintf (StateNamePtr = StateNameArray[0], "%04.04X", JpiState);

      vecptr = FaoVector;
      *vecptr++ = ADMIN_REPORT_SHOW_PROCESS;
      *vecptr++ = JpiPid;
      *vecptr++ = JpiUserName;
      *vecptr++ = ProcessCount;
      *vecptr++ = sptr;
      *vecptr++ = JpiPid;
      *vecptr++ = sptr;
      *vecptr++ = JpiUserName;
      *vecptr++ = sptr;
      *vecptr++ = JpiPrcNam;
      *vecptr++ = sptr;
      if (*cptr)
         *vecptr++ = cptr;
      else
         *vecptr++ = "[<FONT SIZE=-1>DCL</FONT>]";
      *vecptr++ = sptr;
      switch (JpiMode)
      {
         case JPI$K_BATCH : *vecptr++ = "BAT"; break;
         case JPI$K_INTERACTIVE : *vecptr++ = "INT"; break;
         case JPI$K_NETWORK : *vecptr++ = "NET"; break;
         case JPI$K_OTHER : *vecptr++ = "OTH"; break;
         default : *vecptr++ = "?";
      }
      *vecptr++ = sptr;
      *vecptr++ = StateNamePtr;
      *vecptr++ = sptr; 
      *vecptr++ = JpiPri;
      *vecptr++ = JpiPrib;

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

   if (DclScriptDetachProcess)
      if (VMSnok (SetPrvStatus = sys$setprv (0, &MailboxMask, 0, 0)))
         ErrorExitVmsStatus (SetPrvStatus, "sys$setprv()", FI_LI);

   if (status != SS$_NOMOREPROC)
   {
      rqptr->rqResponse.ErrorTextPtr = "sys$getjpiw()";
      ErrorVmsStatus (rqptr, status, FI_LI);
   }

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

   SysDclAst (NextTaskFunction, rqptr);
}

/*****************************************************************************/
/*
Using a scripting script process do a SHOW PROCESS /ALL on the specified
process.  Used from the DclReport() but actually could be used on any process
the server has access to, including the server!
*/

WatchShowProcess
(
REQUEST_STRUCT *rqptr,
REQUEST_AST NextTaskFunction,
char *ProcessIdString,
char *ProcessIdUserName
)
{
   static char  DclCommand [512];
   static unsigned long  JpiServerPid;
   static $DESCRIPTOR (DclCommandDsc, DclCommand);
   static $DESCRIPTOR (DclCommandFaoDsc,
"SHOW PROCESS /ALL /IDENT=!AZ\n\
SV=$SEVERITY\n\
IF SV THEN MO=F$GETJPI(\"!AZ\",\"MODE\")\n\
JT=\"\"\n\
IF SV THEN IF F$GETJPI(\"!AZ\",\"PID\").NES.F$GETJPI(\"!AZ\",\"MASTER_PID\") \
THEN JT=\" (subprocess)\"\n\
IF SV THEN IF JT.EQS.\"\".AND.F$GETJPI(\"!AZ\",\"JOBTYPE\").EQ.0 \
THEN JT=\" (detached)\"\n\
IF SV THEN IM=F$GETJPI(\"!AZ\",\"IMAGNAME\")\n\
IF SV THEN IF IM.EQS.\"\" THEN IM=\"[DCL]\"\n\
LF[0,8]=10\n\
IF SV THEN WRITE SYS$OUTPUT LF+\"Mode: \"+MO+JT+LF+LF+\"Image: \"+IM\n\
\0");

   static char  BeginPage [] =
"<P><TABLE CELLSPACING=0 CELLPADDING=5 BORDER=1>\n\
<TR><TD>\n\
<TABLE CELLSPACING=0 CELLPADDING=5 BORDER=0>\n\
<TR><TD><PRE>";

   int  status;
   unsigned long  *vecptr;
   unsigned long  ProcessId;
   unsigned long  FaoVector [32];
   char  *cptr, *sptr;
   REQUEST_AST EndPageFunction;

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

   if (Debug) fprintf (stdout, "WatchShowProcess() %s\n", ProcessIdString);

   if (ProcessIdUserName[0])
   {
      if (strsame (ProcessIdUserName, HttpdScriptAsUserName, -1))
         rqptr->rqPathSet.ScriptAsPtr = HttpdScriptAsUserName;
      else
      if (strsame (ProcessIdUserName, HttpdProcess.UserName, -1))
         rqptr->rqPathSet.ScriptAsPtr = HttpdProcess.UserName;
      else
      {
         if (!DclPersonaServicesAvailable)
         {
            rqptr->rqResponse.HttpStatus = 403;
            ErrorGeneral (rqptr, ErrorWatchPersonaNeeded, FI_LI);
            SysDclAst (NextTaskFunction, rqptr);
            return;
         }
         rqptr->rqPathSet.ScriptAsPtr = cptr =
            VmGetHeap (rqptr, strlen(ProcessIdUserName)+1);
         strcpy (cptr, ProcessIdUserName);
      }
   }

   ProcessId = strtol (ProcessIdString, NULL, 16);
   if (Debug) fprintf (stdout, "ProcessId: %X08.08X\n", ProcessId);

   /* suppress the [delete] button for the main server process!! */
   if (ProcessId == HttpdProcess.Pid)
      EndPageFunction = &WatchShowEnd;
   else
      EndPageFunction = &WatchShowProcessDeleteEnd;

   rqptr->WatchShowNextTaskFunction = NextTaskFunction;

   vecptr = FaoVector;
   *vecptr++ = ProcessIdString;
   *vecptr++ = ProcessIdString;
   *vecptr++ = ProcessIdString;
   *vecptr++ = ProcessIdString;
   *vecptr++ = ProcessIdString;
   *vecptr++ = ProcessIdString;

   status = sys$faol (&DclCommandFaoDsc, 0, &DclCommandDsc, &FaoVector);
   if (VMSnok (status) || status == SS$_BUFFEROVF)
   {
      rqptr->rqResponse.ErrorTextPtr = "sys$faol()";
      ErrorVmsStatus (rqptr, status, FI_LI);
      SysDclAst (NextTaskFunction, rqptr);
      return;
   }
   if (Debug) fprintf (stdout, "|%s|\n", DclCommand);

   rqptr->rqResponse.PreExpired = PRE_EXPIRE_ADMIN;
   RESPONSE_HEADER_200_HTML (rqptr);
   AdminPageTitle (rqptr, "Show Process", BeginPage);

   rqptr->rqCgi.BufferRecords =
      rqptr->rqOutput.BufferEscapeHtml = true;

   DclBegin (rqptr, EndPageFunction, DclCommand,
             NULL, NULL, NULL, NULL, NULL);
}

/*****************************************************************************/
/*
Called when the scripting script process is complete.  Output the last portion
of the report page and AST to wherever was the buffered end-of-report function.
*/

WatchShowProcessDeleteEnd (REQUEST_STRUCT *rqptr)

{
   static char  EndPageFao [] =
"</PRE></TD></TR>\n\
</TABLE>\n\
</TD></TR>\n\
</TABLE>\n\
<P><FORM METHOD=GET ACTION=\"!AZ\">\n\
<INPUT TYPE=hidden NAME=pid VALUE=\"!AZ\">\n\
<INPUT TYPE=submit VALUE=\" Force Delete \">\n\
</FORM>\n\
</BODY>\n\
</HTML>\n";

   int  status;
   unsigned long  *vecptr;
   unsigned long  FaoVector [8];
   char  ProcessIdString [32];

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

   if (Debug) fprintf (stdout, "WatchShowProcessDeleteEnd()\n");

   rqptr->rqOutput.BufferEscapeHtml = false;

   if (!strsame (rqptr->rqHeader.QueryStringPtr, "pid=", 4))
   {
      WatchShowEnd (rqptr);
      return;
   }
   strzcpy (ProcessIdString, rqptr->rqHeader.QueryStringPtr+4, sizeof(ProcessIdString));

   vecptr = FaoVector;
   *vecptr++ = ADMIN_CONTROL_DELETE_PROCESS;
   *vecptr++ = ProcessIdString;

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

   SysDclAst (rqptr->WatchShowNextTaskFunction, rqptr);
}

/*****************************************************************************/
/*
Using a scripting script process to display relevant system details.
*/

WatchShowSystem
(
REQUEST_STRUCT *rqptr,
REQUEST_AST NextTaskFunction
)
{
   static char  DclCommand [] =
"SAY=\"WRITE SYS$OUTPUT\"\n\
H80=\"\'\'SAY\' F$FAO(\"\"!/!80*-!/\"\")\"\n\
V62=F$GETSYI(\"VERSION\").GES.\"V6.2\"\n\
SYI=F$FAO(\"!AS, a !AS with !UL CPU and !ULMB running VMS !AS\",\
F$GETSYI(\"NODENAME\"),F$GETSYI(\"HW_NAME\"),F$GETSYI(\"AVAILCPU_CNT\"),\
(F$GETSYI(\"MEMSIZE\")*(F$GETSYI(\"PAGE_SIZE\")/512)/2048),\
F$EDIT(F$GETSYI(\"VERSION\"),\"COLLAPSE\"))\n\
HDR=\"\"\n\
IF V62 THEN \
HDR=F$FAO(\"  Pid    Process Name    State  Pri      I/O\
       CPU       Page flts  Pages!/\")\n\
SAY SYI\n\
SAY F$FAO(\"!#*-!/!/!AS\",F$LENGTH(SYI),HDR)\n\
SYS=\"SHOW SYSTEM/FULL\"\n\
IF V62 THEN SYS=SYS+\"/NOHEAD\"\n\
SYS\n\
H80\n\
SAY \"\"\n\
SHUSER=\"SHOW USER/NODE/INT/NET/BAT/SUB\"\n\
SHUSERF=SHUSER+\"/FULL\"\n\
IF V62 THEN SHUSERF=SHUSERF+\"/NOHEAD\"\n\
SHUSER\n\
SAY \"\"\n\
SHUSERF\n\
H80\n\
SAY \"\"\n\
SHOW MEMORY/FULL\n\
H80\n\
SHOW CPU/FULL\n\
H80\n\
SHOW DEVICE D\n\
DEFINE/USER SYS$ERROR NL:\n\
DEFINE/USER SYS$OUTPUT NL:\n\
SHOW ERROR\n\
OK=F$INTEGER(F$EXTRACT(3,7,$STATUS)).EQ.1\n\
IF OK THEN SAY \"\"\n\
IF OK THEN SHOW ERROR\n\
SHNET=\"SHOW NET\"\n\
IF V62 THEN H80\n\
IF V62 THEN SHNET\n\
IF v62 THEN SHNET/FULL";

   static char  BeginPage [] =
"<P><TABLE CELLSPACING=0 CELLPADDING=5 BORDER=1>\n\
<TR><TD>\n\
<TABLE CELLSPACING=0 CELLPADDING=5 BORDER=0>\n\
<TR><TD><PRE>";

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

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

   if (Debug) fprintf (stdout, "WatchShowSystem()\n");

   rqptr->WatchShowNextTaskFunction = NextTaskFunction;

   rqptr->rqResponse.PreExpired = PRE_EXPIRE_ADMIN;
   RESPONSE_HEADER_200_HTML (rqptr);
   AdminPageTitle (rqptr, "System Report", BeginPage);

   /* filter-null required, V7.3 (at least) SHOW CPU/FULL contains them!! */
   rqptr->rqCgi.BufferRecords = 
      rqptr->rqCgi.FilterStream =
      rqptr->rqOutput.BufferEscapeHtml = true;

   DclBegin (rqptr, &WatchShowEnd, DclCommand,
             NULL, NULL, NULL, NULL, NULL);
}

/*****************************************************************************/
/*
Called when the scripting script process is complete.  Output the last portion
of the report page and AST to wherever was the buffered end-of-report function.
*/

WatchShowEnd (REQUEST_STRUCT *rqptr)

{
   static char  EndPageFao [] =
"</PRE></TD></TR>\n\
</TABLE>\n\
</TD></TR>\n\
</TABLE>\n\
</BODY>\n\
</HTML>\n";

   int  status;

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

   if (Debug) fprintf (stdout, "WatchShowEnd()\n");

   rqptr->rqOutput.BufferEscapeHtml = false;

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

   SysDclAst (rqptr->WatchShowNextTaskFunction, rqptr);
}

/*****************************************************************************/
/*
Just delete the process specified by 'ProcessIdString'.
*/

WatchDeleteProcess
(
REQUEST_STRUCT *rqptr,
REQUEST_AST NextTaskFunction
)
{
   int  status,
        SetPrvStatus;
   unsigned long  ProcessId;

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

   if (Debug)
      fprintf (stdout, "WatchDeleteProcess() |%s|\n",
               rqptr->rqHeader.QueryStringPtr);

   if (strsame (rqptr->rqHeader.QueryStringPtr, "pid=", 4))
   {
      ProcessId = strtol (rqptr->rqHeader.QueryStringPtr+4, NULL, 16);
      if (Debug) fprintf (stdout, "ProcessID: %08.08X\n", ProcessId);
   }
   else
      ProcessId  = 0;

   if (ProcessId)
   {
      if (DclScriptDetachProcess)
      {
         /* detached scripts, possibly executing as a non-server username */
         if (VMSnok (SetPrvStatus = sys$setprv (1, &WorldMask, 0, 0)))
            ErrorExitVmsStatus (SetPrvStatus, "sys$setprv()", FI_LI);
         status = sys$delprc (&ProcessId, 0);
         if (VMSnok (SetPrvStatus = sys$setprv (0, &WorldMask, 0, 0)))
            ErrorExitVmsStatus (SetPrvStatus, "sys$setprv()", FI_LI);
      }
      else
         status = sys$delprc (&ProcessId, 0);
      if (Debug) fprintf (stdout, "sys$dlprc %%X%08.08X\n", status);
   }
   else
      status = SS$_BUGCHECK;

   if (VMSnok (status))
   {
      rqptr->rqResponse.HttpStatus = 409;
      rqptr->rqResponse.ErrorTextPtr = "when deleting";
      ErrorVmsStatus (rqptr, status, FI_LI);
      SysDclAst (NextTaskFunction, rqptr);
      return;
   }

   rqptr->rqResponse.PreExpired = PRE_EXPIRE_ADMIN;
   ReportSuccess (rqptr, "Server !AZ deleted process !8XL.",
                  ServerHostPort, ProcessId);

   SysDclAst (NextTaskFunction, rqptr);
}

/*****************************************************************************/
/*
Called from WatchBegin().  Provide a plain-text report displaying some of the
essential fields from various data structures in an executing request. 
Intended as a diagnosis and development tool.
*/

WatchPeek
(
REQUEST_STRUCT *rqptr,
REQUEST_STRUCT *rqeptr
)
{
#define WATCH_NULL_STRING "(null)"
#define WATCH_NULL(string) (!string ? WATCH_NULL_STRING : string)

   static long  Subx2 = 2,
                EdivTen = 10;

   static char  ServiceFao [] =
"\n\
!33<TimerTickSecond!> !UL\n\
!33<ServicePtr!> !&X\n\
!33<->ServerChannel!> !UL (!AZ)\n\
!33<->ServerHostPort!> !&Z\n\
!33<->ServerIpAddressString!> !&Z\n\
!33<->RequestSchemeNamePtr!> !&Z\n\
!33<->SSLsessionPtr!> !&X\n\
!33<->SSLclientPtr!> !&X\n\
\n\
!33<->NotePadPtr!> !&Z\n\
!33<->ProxyReverseLocationPtr!> !&Z\n\
\n\
!33<rqClient.Channel!> !UL (!AZ)\n\
!33<rqClient.Lookup.HostName!> !&Z\n\
!33<rqClient.IpAddress!> !&I\n\
!33<rqClient.IpPort!> !UL\n\
!33<rqNet.ReadRawAstFunction!> !&A\n\
!33<rqNet.ReadIOsb!> !&S !UL\n\
!33<rqNet.ReadErrorCount!> !UL\n\
!33<rqNet.ReadErrorStatus!> !&S\n\
!33<rqNet.WriteRawAstFunction!> !&A\n\
!33<rqNet.WriteIOsb!> !&S !UL\n\
!33<rqNet.WriteErrorCount!> !UL\n\
!33<rqNet.WriteErrorStatus!> !&S\n\
!33<SesolaPtr!> !&X\n";

   static char  TimerFao [] =
"!33<BytesRx!> !UL\n\
!33<BytesTx!> !UL\n\
!33<BytesRawRx!> !UL\n\
!33<BytesRawTx!> !UL\n\
!33<rqNet.KeepAliveCount!> !UL\n\
!33<KeepAliveRequest!> !&B\n\
!33<KeepAliveResponse!> !&B\n\
!33<KeepAliveTimeout!> !&B\n\
!33<rqTmr.InputSecond!> !UL\n\
!33<rqTmr.KeepAliveSecond!> !UL\n\
!33<rqTmr.ListIndex!> !UL!&@\n\
!33<rqTmr.NoProgressBytesTx!> !UL\n\
!33<rqTmr.NoProgressSecond!> !UL\n\
!33<rqTmr.NoProgressPeriod!> !UL\n\
!33<rqTmr.OutputSecond!> !UL\n\
!33<rqTmr.TerminatedCount!> !UL\n\
!33<rqTmr.ThrottleSecond!> !UL\n\
!33<rqPathSet.ThrottleFrom!> !UL!AZ\n\
!33<rqPathSet.ThrottleTo!> !UL\n\
!33<rqPathSet.ThrottleResume!> !UL\n\
!33<rqPathSet.ThrottleBusy!> !UL\n\
!33<rqPathSet.ThrottleIndex!> !UL!&@\n\
\n\
!33<rqTime.Vms64bit!> !%D (!UL.!#ZL seconds ago)\n\
!33<rqTime.GmDateTime!> !&Z\n\
!33<rqHeader.RequestHeaderPtr!> |!#AZ|\n\
!33<rqHeader.MethodName!> !&Z\n\
!33<rqHeader.RequestUriPtr!> {!UL}!&P\n\
!33<rqHeader.HttpVersion!> !UL.!UL\n\
!33<rqHeader.AcceptPtr!> !&Z\n\
!33<rqHeader.AcceptCharsetPtr!> !&Z\n\
!33<rqHeader.AcceptEncodingPtr!> !&Z\n\
!33<rqHeader.AcceptLangPtr!> !&Z\n\
!33<rqHeader.AuthorizationPtr!> !&Z\n\
!33<rqHeader.CacheControlPtr!> !&Z\n\
!33<rqHeader.ContentLength!> !UL\n\
!33<rqHeader.ContentTypePtr!> !&Z\n\
!33<rqHeader.CookiePtr!> !&Z\n\
!33<rqHeader.ETagPtr!> !&Z\n\
!33<rqHeader.ForwardedPtr!> !&Z\n\
!33<rqHeader.HostPtr!> !&Z\n\
!33<rqHeader.IfModifiedSincePtr!> !&Z (!&W)\n\
!33<rqHeader.KeepAlivePtr!> !&Z\n\
!33<rqHeader.PragmaPtr!> !&Z\n\
!33<rqHeader.ProxyAuthorizationPtr!> !&Z\n\
!33<rqHeader.ProxyConnectionPtr!> !&Z\n\
!33<rqHeader.RangePtr!> !&Z\n\
!33<rqHeader.RefererPtr!> !&Z\n\
!33<rqHeader.UserAgentPtr!> !&Z\n\
!33<rqHeader.XForwardedForPtr!> !&Z\n\
!33<rqHeader.UnknownFieldsCount!> !UL\n\
!33<rqBody.ContentLength!> !UL\n\
!33<rqBody.ContentCount!> !UL\n\
!33<rqBody.DataVBN!> !UL\n\
!33<rqBody.DataPtr!> 0x!XL\n\
!33<rqBody.DataCount!> !UL\n\
!33<rqBody.DataSize!> !UL\n\
!33<rqBody.DataStatus!> !&S\n\
!33<rqBody.DiscardChunkCount!> !UL\n\
!33<rqBody.AstFunction!> !&A\n\
!33<rqBody.ProcessFunction!> !&A\n\
!33<rqBody.ProcessPtr!> !&X\n\
\n\
!33<rqHeader.PathInfoPtr!> !&Z\n\
!33<rqHeader.QueryStringPtr!> !&Z\n\
!33<Md5HashPath!> |!16&H|\n\
!33<MappedPathPtr!> !&Z\n\
!33<RequestMappedFile!> !&Z\n\
!33<ParseOds.ExpFileName!> !&Z\n\
!33<ScriptName!> !&Z\n\
!33<RequestMappedScript!> !&Z\n\
!33<RequestMappedRunTime!> !&Z\n\
!33<IsCgiPlusScript!> !&B\n\
!33<rqPathSet.PathOds!> !UL !AZ\n\
!33<PathOds!> !UL !AZ\n\
!33<PathOdsExtended!> !&B\n\
\n\
!33<rqResponse.HeaderPtr!> |!#AZ|\n\
!33<rqResponse.HeaderSent!> !&B\n\
!33<rqResponse.ErrorReportPtr!> !&Z\n\
!33<rqResponse.LocationPtr!> !&Z\n\
\n\
!33<RemoteUser!> !&Z\n\
!33<RemoteUserPassword!> |!#**|\n\
!33<ResolvedRemoteUser!> !&B\n\
!33<rqAuth.FinalStatus!> !&S\n\
!33<rqAuth.RequestCan!> 0x!4XL (!AZ)\n\
!33<rqAuth.UserCan!> 0x!4XL (!AZ)\n\
!33<rqAuth.GroupCan!> 0x!4XL (!AZ)\n\
!33<rqAuth.WorldCan!> 0x!4XL (!AZ)\n\
!33<rqAuth.Type!> !&Z\n\
!33<rqAuth.UserDetailsPtr!> !&Z\n\
!33<rqAuth.HttpsOnly!> !&B\n\
!33<rqAuth.SkelKeyAuthenticated!> !&B\n\
!33<rqAuth.SysUafAuthenticated!> !&B\n\
!33<rqAuth.VmsIdentifiersCount!> !UL\n\
!33<rqAuth.VmsIdentifiersPtr!> 0x!XL\n\
!33<rqAuth.VmsUserProfile!> !&B\n\
!33<rqAuth.VmsUserProfileLength!> !UL\n\
!33<rqAuth.VmsUserProfilePtr!> 0x!XL\n\
!33<rqAuth.VmsUserScriptAs!> !&B\n\
!33<rqAuth.DirectoryPtr!> !&Z\n\
!33<rqAuth.RealmPtr!> !&Z (!AZ)\n\
!33<rqAuth.RealmDescrPtr!> !&Z\n\
!33<rqAuth.PathParameterPtr!> !&Z\n\
!33<rqAuth.GroupWritePtr!> !&Z (!AZ)\n\
!33<rqAuth.GroupReadPtr!> !&Z (!AZ)\n\
!33<rqAuth.GroupRestrictListPtr!> !&Z\n\
!33<rqAuth.WorldRestrictListPtr!> !&Z\n\
!33<rqAuth.ProxyStringPtr!> !&Z\n\
!33<rqAuth.RemoteUser!> !&Z\n\
\n\
!33<rqCgi.BufferLength!> !UL\n\
!33<rqCgi.BufferRemaining!> !UL\n\
!33<rqCgi.BufferPtr!> 0x!XL\n\
!33<rqCgi.BufferCurrentPtr!> 0x!XL\n\
!33<rqCgi.CalloutInProgress!> !&B\n\
!33<rqCgi.CalloutOutputCount!> !UL\n\
!33<rqCgi.CalloutOutputPtr!> 0x!XL !&Z\n\
!33<rqCgi.ContentTypeText!> !&B\n\
!33<rqCgi.EofPtr!> !&Z\n\
!33<rqCgi.EotPtr!> !&Z\n\
!33<rqCgi.EscPtr!> !&Z\n\
!33<rqCgi.HeaderLineCount!> !UL\n\
!33<rqCgi.IsCliDcl!> !&B\n\
!33<rqCgi.OutputMode!> !UL (!AZ)\n\
!33<rqCgi.ProcessingBody!> !&B\n\
!33<rqCgi.RecordCount!> !UL\n\
!33<rqCgi.ScriptRetryCount!> !UL\n\
!33<rqCgi.XVMSRecordMode!> !&B\n\
";

   static char  CacheFao [] =
"\n\
!33<rqCache.EntryPtr!> 0x!XL\n\
!33<DclTaskPtr!> 0x!XL\n\
";

   static char  DclTaskFao [] =
"!33<->TaskType!> !UL (!AZ)\n\
!33<->ScriptProcessPid!> !8XL\n\
!33<->LifeTimeSecond!> !&@\n\
!33<->CrePrcTermMbxChannel!> !UL (!AZ)\n\
!33<->CrePrcDetachProcess!> !UL\n\
!33<->CrePrcDetachStarting!> !UL\n\
!33<->CrePrcUserName!> !&Z\n\
!33<->CgiPlusVarStruct!> !UL\n\
!33<->CgiPlusInChannel!> !UL (!AZ)\n\
!33<->CgiPlusInIOsb!> !&S !UL\n\
!33<->QueuedCgiPlusIn!> !UL\n\
!33<->HttpInputChannel!> !UL (!AZ)\n\
!33<->HttpInputIOsb!> !&S !UL\n\
!33<->QueuedHttpInput!> !UL\n\
!33<->ClientReadBufferSize!> !UL\n\
!33<->ClientReadStripCrLf!> !UL\n\
!33<->QueuedClientRead!> !UL\n\
!33<->SysCommandChannel!> !UL (!AZ)\n\
!33<->SysCommandIOsb!> !&S !UL\n\
!33<->QueuedSysCommand!> !UL\n\
!33<->QueuedSysCommandAllowed!> !UL\n\
!33<->SysOutputSize!> !UL\n\
!33<->SysOutputChannel!> !UL (!AZ)\n\
!33<->SysOutputIOsb!> !&S !UL\n\
!33<->QueuedSysOutput!> !UL\n\
!33<->BuildRecords!> !&B\n\
!33<->SysOutputBuildCount!> !UL\n\
!33<->ClientWriteErrorCount!> !UL\n\
!33<->ScriptProcessPid!> !8XL\n\
!33<->CrePrcTermRecord.acc$l_finalsts!> !&S\n\
!33<->ScriptProcessActivated!> !&B\n\
!33<->ScriptProcessResponded!> !&B\n\
!33<->TaskRunDown!> !&B\n\
!33<->DeleteProcess!> !&B\n\
!33<->ForceImageExit!> !&B\n\
!33<->ForceImageExitGetJpi!> !&B\n\
!33<->ForceImageExitIssued!> !&B\n\
!33<->ForceImageExitSecond!> !UL\n\
!33<->JpiImagNameIOsb.Status!> !&S\n\
!33<->JpiImagName!> |!#AZ|\n\
!33<->ScriptCpuMax!> !UL\n\
!33<->ScriptCpuTimGetJpi!> !UL\n\
!33<->JpiCpuTimIOsb.Status!> !&S\n\
!33<->JpiCpuTim!> !UL\n\
!33<->ScriptCpuTimMax!> !UL\n\
!33<->CalloutFunction!> !&A\n\
!33<->DclCommandPtr!> !&Z\n\
!33<->DclCommandSize!> !UL\n\
!33<->DclCommandLength!> !UL\n\
!33<->ScriptName!> !&Z\n\
!33<->ScriptFileName!> !&Z\n\
\n";

   static char  DECnetFao [] = "!33<DECnetTaskPtr!> !&X\n";

   static char  DECnetTaskFao [] =
"!33<->DECnetChannel!> !UL\n\
!33<->DECnetConnectIOsb!> !&S !UL\n\
!33<->DECnetReadIOsb!> !&S !UL\n\
!33<->DECnetWriteIOsb!> !&S !UL\n\
!33<->QueuedDECnetIO!> !UL\n\
!33<->ScriptResponded!> !&B\n\
!33<->BuildRecords!> !&B\n\
!33<->BuildCount!> !UL\n\
!33<->CgiDialogState!> !UL\n\
!33<->OsuDialogState!> !UL\n\
\n";

   static char  DescrFao [] =
"!33<DescrTaskPtr!> !&X\n\
!33<DirTaskPtr!> !&X\n\
!33<FileTaskPtr!> !&X\n\
!33<GraphTaskPtr!> !&X\n\
!33<HTAdminTaskPtr!> !&X\n\
!33<IsMapTaskPtr!> !&X\n\
!33<MenuTaskPtr!> !&X\n\
!33<ProxyTaskPtr!> !&X\n\
";

   static char  ProxyTaskFao [] =
"!33<->ProxyChannel!> !UL (!AZ)\n\
!33<->RequestHostPort!> !&Z\n\
!33<->RequestSchemeName!> !&Z\n\
!33<->ProxyLookupRetryCount!> !UL\n\
!33<->ProxyConnectIOsb!> !&S !UL\n\
!33<->ProxyReadIOsb!> !&S !UL\n\
!33<->ProxyReadRawAstFunction!> !&A\n\
!33<->ProxyWriteIOsb!> !&S !UL\n\
!33<->ProxyWriteRawAstFunction!> !&A\n\
!33<->BytesRawRx!> !UL\n\
!33<->BytesRawTx!> !UL\n\
!33<->VerifyRecordPtr!> !&Z\n\
!33<->RebuiltRequestPtr!> !&Z\n\
!33<->ResponseConsecutiveNewLineCount!> !UL\n\
!33<->ResponseHeaderLength!> !UL\n\
!33<->ResponseHeaderPtr!> |!#AZ|\n\
!33<->ResponseContentLength!> !UL\n\
!33<->ResponseBodyLength!> !UL\n\
!33<->ResponseBufferNetCount!> !UL\n\
!33<->ResponseBufferNetPtr!> !&X\n\
!33<->ResponseBufferCacheCount!> !UL\n\
!33<->ResponseBufferCachePtr!> !&X\n\
!33<->NotCacheable!> !&B\n\
!33<->ProxyCacheFileName!> !&Z\n\
!33<->ProxyCacheSuitable!> !&B\n\
!33<->ProxyCacheFileSizeInBytes!> !UL\n\
!33<->ProxyCacheLastModifiedHours!> !UL\n\
!33<->ProxyCacheReadBytes!> !UL\n\
!33<->ProxyCacheFileCdt!> !%D\n\
";

   static char  PutFao [] =
"!33<PutTaskPtr!> !&X\n\
!33<SsiTaskPtr!> !&X\n\
!33<UpdTaskPtr!> !&X\n\
\n";

   int  status,
        ConnectNumber;
   unsigned short  Length;
   unsigned long  Remainder,
                  ResponseDuration,
                  Seconds,
                  SubSeconds,
                  WatchModule;
   unsigned long  BinaryTime [2],
                  FaoVector [256],
                  ResultTime [2];
   unsigned long  *vecptr;
   char  *cptr;
   char  Buffer [8192],
         ClientDevName [64],
         ProxyDevName [64],
         ServerDevName [64];
   MAP_RULE_META  *mrptr;

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

   if (Debug) fprintf (stdout, "WatchPeek()\n");

   WatchModule = Watch.Module;
   Watch.Module = 0;

   NetGetBgDevice (rqeptr->ServicePtr->ServerChannel,
                   ServerDevName, sizeof(ServerDevName));

   NetGetBgDevice (rqeptr->rqClient.Channel,
                   ClientDevName, sizeof(ClientDevName));

   sys$gettim (&BinaryTime);
   status = lib$subx (&BinaryTime, &rqeptr->rqTime.Vms64bit, 
                      &ResultTime, &Subx2);
   /* convert to microseconds (longword becomes 71 minutes max) */
   if (VMSok (status))
      status = lib$ediv (&EdivTen, &ResultTime,
                         &ResponseDuration, &Remainder);
   if (Debug) fprintf (stdout, "elapsed: %%X%08.08X\n", status);
   if (VMSnok (status)) ResponseDuration = 0;

   vecptr = FaoVector;

   *vecptr++ = HttpdTickSecond;
   *vecptr++ = rqeptr->ServicePtr;
   *vecptr++ = rqeptr->ServicePtr->ServerChannel;
   *vecptr++ = ServerDevName+1;
   *vecptr++ = rqeptr->ServicePtr->ServerHostPort;
   *vecptr++ = rqeptr->ServicePtr->ServerIpAddressString;
   *vecptr++ = rqeptr->ServicePtr->RequestSchemeNamePtr;
   *vecptr++ = rqeptr->ServicePtr->SSLserverPtr;
   *vecptr++ = rqeptr->ServicePtr->SSLclientPtr;

   *vecptr++ = rqeptr->NotePadPtr;
   *vecptr++ = rqeptr->ProxyReverseLocationPtr;

   *vecptr++ = rqeptr->rqClient.Channel;
   *vecptr++ = ClientDevName+1;
   *vecptr++ = rqeptr->rqClient.Lookup.HostName;
   *vecptr++ = &rqeptr->rqClient.IpAddress;
   *vecptr++ = rqeptr->rqClient.IpPort;
   *vecptr++ = rqeptr->rqNet.ReadRawAstFunction;
   *vecptr++ = rqeptr->rqNet.ReadIOsb.Status;
   *vecptr++ = rqeptr->rqNet.ReadIOsb.Count;
   *vecptr++ = rqeptr->rqNet.ReadErrorCount;
   *vecptr++ = rqeptr->rqNet.ReadErrorStatus;
   *vecptr++ = rqeptr->rqNet.WriteRawAstFunction;
   *vecptr++ = rqeptr->rqNet.WriteIOsb.Status;
   *vecptr++ = rqeptr->rqNet.WriteIOsb.Count;
   *vecptr++ = rqeptr->rqNet.WriteErrorCount;
   *vecptr++ = rqeptr->rqNet.WriteErrorStatus;
   *vecptr++ = rqeptr->rqNet.SesolaPtr;

   status = WriteFaol (Buffer, sizeof(Buffer), &Length, ServiceFao, &FaoVector);
   if (VMSnok (status) || status == SS$_BUFFEROVF)
      ErrorNoticed (status, "WriteFaol()", FI_LI);
   NetWrite (rqptr, 0, Buffer, Length);

   SesolaWatchPeek (rqptr, rqeptr);

   vecptr = FaoVector;

   *vecptr++ = rqeptr->BytesRx;
   *vecptr++ = rqeptr->BytesTx;
   *vecptr++ = rqeptr->BytesRawRx;
   *vecptr++ = rqeptr->BytesRawTx;
   *vecptr++ = rqeptr->rqNet.KeepAliveCount;
   *vecptr++ = rqeptr->KeepAliveRequest;
   *vecptr++ = rqeptr->KeepAliveResponse;
   *vecptr++ = rqeptr->KeepAliveTimeout;
   *vecptr++ = rqeptr->rqTmr.InputSecond;
   *vecptr++ = rqeptr->rqTmr.KeepAliveSecond;
   *vecptr++ = rqeptr->rqTmr.ListIndex;
   if (rqeptr->rqTmr.ListIndex)
   {
      *vecptr++ = " (!UL seconds)";
      *vecptr++ = SupervisorListArray[rqeptr->rqTmr.ListIndex].ChunkSeconds;
   }
   else
      *vecptr++ = "";
   *vecptr++ = rqeptr->rqTmr.NoProgressBytesTx;
   *vecptr++ = rqeptr->rqTmr.NoProgressSecond;
   *vecptr++ = rqeptr->rqTmr.NoProgressPeriod;
   *vecptr++ = rqeptr->rqTmr.OutputSecond;
   *vecptr++ = rqeptr->rqTmr.TerminatedCount;
   *vecptr++ = rqeptr->rqTmr.ThrottleSecond;

   *vecptr++ = rqeptr->rqPathSet.ThrottleFrom;
   if (rqeptr->rqPathSet.ThrottleFrom)
   {
      if (!rqeptr->ThrottleListEntry.DataPtr)
         *vecptr++ = " (QUEUED)";
      else
         *vecptr++ = " (PROCESSING)";
   }
   else
      *vecptr++ = "";
   *vecptr++ = rqeptr->rqPathSet.ThrottleTo;
   *vecptr++ = rqeptr->rqPathSet.ThrottleResume;
   *vecptr++ = rqeptr->rqPathSet.ThrottleBusy;
   *vecptr++ = rqeptr->rqPathSet.ThrottleIndex;
   /* if throttled get the path rule using the index number */
   if (rqeptr->rqPathSet.ThrottleIndex)
      mrptr = MapUrl_ThrottleRule (rqeptr->rqPathSet.ThrottleIndex);
   else
      mrptr = NULL;
   if (mrptr)
   {
      *vecptr++ =
" (!AZ throttle=!UL,!UL,!UL,!UL,!2ZL:!2ZL:!2ZL,!2ZL:!2ZL:!2ZL)";
      *vecptr++ = mrptr->TemplatePtr;
      *vecptr++ = mrptr->mpPathSet.ThrottleFrom;
      *vecptr++ = mrptr->mpPathSet.ThrottleTo;
      *vecptr++ = mrptr->mpPathSet.ThrottleResume;
      *vecptr++ = mrptr->mpPathSet.ThrottleBusy;
      *vecptr++ = mrptr->mpPathSet.ThrottleTimeoutQueue / 3600;
      *vecptr++ = (mrptr->mpPathSet.ThrottleTimeoutQueue % 3600) / 60;
      *vecptr++ = (mrptr->mpPathSet.ThrottleTimeoutQueue % 3600) % 60;
      *vecptr++ = mrptr->mpPathSet.ThrottleTimeoutBusy / 3600;
      *vecptr++ = (mrptr->mpPathSet.ThrottleTimeoutBusy % 3600) / 60;
      *vecptr++ = (mrptr->mpPathSet.ThrottleTimeoutBusy % 3600) % 60;
   }
   else
      *vecptr++ = "";

   *vecptr++ = rqeptr->rqTime.Vms64bit;

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

   *vecptr++ = rqeptr->rqTime.GmDateTime;
   if (!rqeptr->rqHeader.RequestHeaderPtr)
      *vecptr++ = sizeof(WATCH_NULL_STRING)-1;
   else
      *vecptr++ = rqeptr->rqHeader.RequestHeaderLength;
   *vecptr++ = WATCH_NULL(rqeptr->rqHeader.RequestHeaderPtr);
   *vecptr++ = rqeptr->rqHeader.MethodName;
   if (!rqeptr->rqHeader.RequestUriPtr)
      *vecptr++ = 0;
   else
      *vecptr++ = strlen(rqeptr->rqHeader.RequestUriPtr);
   *vecptr++ = rqeptr->rqHeader.RequestUriPtr;
   *vecptr++ = rqeptr->rqHeader.HttpVersion / 10;
   *vecptr++ = rqeptr->rqHeader.HttpVersion % 10;
   *vecptr++ = rqeptr->rqHeader.AcceptPtr;
   *vecptr++ = rqeptr->rqHeader.AcceptCharsetPtr;
   *vecptr++ = rqeptr->rqHeader.AcceptEncodingPtr;
   *vecptr++ = rqeptr->rqHeader.AcceptLangPtr;
   *vecptr++ = rqeptr->rqHeader.AuthorizationPtr;
   *vecptr++ = rqeptr->rqHeader.CacheControlPtr;
   *vecptr++ = rqeptr->rqHeader.ContentLength;
   *vecptr++ = rqeptr->rqHeader.ContentTypePtr;
   *vecptr++ = rqeptr->rqHeader.CookiePtr;
   *vecptr++ = rqeptr->rqHeader.ETagPtr;
   *vecptr++ = rqeptr->rqHeader.ForwardedPtr;
   *vecptr++ = rqeptr->rqHeader.HostPtr;
   *vecptr++ = rqeptr->rqHeader.IfModifiedSincePtr;
   *vecptr++ = rqeptr->rqTime.IfModifiedSinceVMS64bit;
   *vecptr++ = rqeptr->rqHeader.KeepAlivePtr;
   *vecptr++ = rqeptr->rqHeader.PragmaPtr;
   *vecptr++ = rqeptr->rqHeader.ProxyAuthorizationPtr;
   *vecptr++ = rqeptr->rqHeader.ProxyConnectionPtr;
   *vecptr++ = rqeptr->rqHeader.RangePtr;
   *vecptr++ = rqeptr->rqHeader.RefererPtr;
   *vecptr++ = rqeptr->rqHeader.UserAgentPtr;
   *vecptr++ = rqeptr->rqHeader.XForwardedForPtr;
   *vecptr++ = rqeptr->rqHeader.UnknownFieldsCount;
   *vecptr++ = rqeptr->rqBody.ContentLength;
   *vecptr++ = rqeptr->rqBody.ContentCount;
   *vecptr++ = rqeptr->rqBody.DataVBN;
   *vecptr++ = rqeptr->rqBody.DataPtr;
   *vecptr++ = rqeptr->rqBody.DataCount;
   *vecptr++ = rqeptr->rqBody.DataSize;
   *vecptr++ = rqeptr->rqBody.DataStatus;
   *vecptr++ = rqeptr->rqBody.DiscardChunkCount;
   *vecptr++ = rqeptr->rqBody.AstFunction;
   *vecptr++ = rqeptr->rqBody.ProcessFunction;
   *vecptr++ = rqeptr->rqBody.ProcessPtr;

   *vecptr++ = rqeptr->rqHeader.PathInfoPtr;
   *vecptr++ = rqeptr->rqHeader.QueryStringPtr;
   *vecptr++ = &rqeptr->Md5HashPath;
   *vecptr++ = rqeptr->MappedPathPtr;
   *vecptr++ = rqeptr->RequestMappedFile;
   *vecptr++ = rqeptr->ParseOds.ExpFileName;
   *vecptr++ = rqeptr->ScriptName;
   *vecptr++ = rqeptr->RequestMappedScript;
   *vecptr++ = rqeptr->RequestMappedRunTime;
   *vecptr++ = rqeptr->IsCgiPlusScript;
   *vecptr++ = rqeptr->rqPathSet.PathOds;
   switch (rqeptr->rqPathSet.PathOds)
   {
      case MAPURL_PATH_ODS_2 : *vecptr++ = "ODS-2"; break;
      case MAPURL_PATH_ODS_5 : *vecptr++ = "ODS-5"; break;
      case MAPURL_PATH_ODS_ADS : *vecptr++ = "ADS"; break;
      case MAPURL_PATH_ODS_PWK : *vecptr++ = "PWK"; break;
      case MAPURL_PATH_ODS_SMB : *vecptr++ = "SMB"; break;
      case MAPURL_PATH_ODS_SRI : *vecptr++ = "SRI"; break;
      default : *vecptr++ = "ods-2";
   }
   *vecptr++ = rqeptr->PathOds;
   switch (rqeptr->PathOds)
   {
      case MAPURL_PATH_ODS_2 : *vecptr++ = "ODS-2"; break;
      case MAPURL_PATH_ODS_5 : *vecptr++ = "ODS-5"; break;
      case MAPURL_PATH_ODS_ADS : *vecptr++ = "ADS"; break;
      case MAPURL_PATH_ODS_PWK : *vecptr++ = "PWK"; break;
      case MAPURL_PATH_ODS_SMB : *vecptr++ = "SMB"; break;
      case MAPURL_PATH_ODS_SRI : *vecptr++ = "SRI"; break;
      default : *vecptr++ = "ods-2";
   }
   *vecptr++ = rqeptr->PathOdsExtended;

   if (!rqeptr->rqResponse.HeaderPtr)
      *vecptr++ = sizeof(WATCH_NULL_STRING)-1;
   else
      *vecptr++ = rqeptr->rqResponse.HeaderLength;
   *vecptr++ = WATCH_NULL(rqeptr->rqResponse.HeaderPtr);
   *vecptr++ = rqeptr->rqResponse.HeaderSent;
   *vecptr++ = rqeptr->rqResponse.ErrorReportPtr;
   *vecptr++ = rqeptr->rqResponse.LocationPtr;

   *vecptr++ = rqeptr->RemoteUser;
   *vecptr++ = strlen(rqeptr->RemoteUserPassword);
   *vecptr++ = rqeptr->rqAuth.ResolvedRemoteUser;
   *vecptr++ = rqeptr->rqAuth.FinalStatus;
   *vecptr++ = rqeptr->rqAuth.RequestCan;
   *vecptr++ = AuthCanString (rqeptr->rqAuth.RequestCan, AUTH_CAN_FORMAT_LONG);
   *vecptr++ = rqeptr->rqAuth.UserCan;
   *vecptr++ = AuthCanString (rqeptr->rqAuth.UserCan, AUTH_CAN_FORMAT_LONG);
   *vecptr++ = rqeptr->rqAuth.GroupCan;
   *vecptr++ = AuthCanString (rqeptr->rqAuth.GroupCan, AUTH_CAN_FORMAT_LONG);
   *vecptr++ = rqeptr->rqAuth.WorldCan;
   *vecptr++ = AuthCanString (rqeptr->rqAuth.WorldCan, AUTH_CAN_FORMAT_LONG);
   *vecptr++ = rqeptr->rqAuth.Type;
   *vecptr++ = rqeptr->rqAuth.UserDetailsPtr;
   *vecptr++ = rqeptr->rqAuth.HttpsOnly;
   *vecptr++ = rqeptr->rqAuth.SkelKeyAuthenticated;
   *vecptr++ = rqeptr->rqAuth.SysUafAuthenticated;
   *vecptr++ = rqeptr->rqAuth.VmsIdentifiersCount;
   *vecptr++ = rqeptr->rqAuth.VmsIdentifiersPtr;
   *vecptr++ = rqeptr->rqAuth.VmsUserProfile;
   *vecptr++ = rqeptr->rqAuth.VmsUserProfileLength;
   *vecptr++ = rqeptr->rqAuth.VmsUserProfilePtr;
   *vecptr++ = rqeptr->rqAuth.VmsUserScriptAs;
   *vecptr++ = rqeptr->rqAuth.DirectoryPtr;
   *vecptr++ = rqeptr->rqAuth.RealmPtr;
   *vecptr++ = AuthSourceString (rqeptr->rqAuth.RealmPtr,
                                 rqeptr->rqAuth.SourceRealm);
   *vecptr++ = rqeptr->rqAuth.RealmDescrPtr;
   *vecptr++ = rqeptr->rqAuth.PathParameterPtr;
   *vecptr++ = rqeptr->rqAuth.GroupWritePtr;
   *vecptr++ = AuthSourceString (rqeptr->rqAuth.GroupWritePtr,
                                 rqeptr->rqAuth.SourceGroupWrite);
   *vecptr++ = rqeptr->rqAuth.GroupReadPtr;
   *vecptr++ = AuthSourceString (rqeptr->rqAuth.GroupReadPtr,
                                 rqeptr->rqAuth.SourceGroupRead);
   *vecptr++ = rqeptr->rqAuth.GroupRestrictListPtr;
   *vecptr++ = rqeptr->rqAuth.WorldRestrictListPtr;
   *vecptr++ = rqeptr->rqAuth.ProxyStringPtr;
   *vecptr++ = rqeptr->rqAuth.RemoteUser;

   *vecptr++ = rqeptr->rqCgi.BufferLength;
   *vecptr++ = rqeptr->rqCgi.BufferRemaining;
   *vecptr++ = rqeptr->rqCgi.BufferPtr;
   *vecptr++ = rqeptr->rqCgi.BufferCurrentPtr;
   *vecptr++ = rqeptr->rqCgi.CalloutInProgress;
   *vecptr++ = rqeptr->rqCgi.CalloutOutputCount;
   *vecptr++ = rqeptr->rqCgi.CalloutOutputPtr;
   *vecptr++ = rqeptr->rqCgi.CalloutOutputPtr;
   *vecptr++ = rqeptr->rqCgi.ContentTypeText;
   *vecptr++ = rqeptr->rqCgi.EofPtr;
   *vecptr++ = rqeptr->rqCgi.EotPtr;
   *vecptr++ = rqeptr->rqCgi.EscPtr;
   *vecptr++ = rqeptr->rqCgi.HeaderLineCount;
   *vecptr++ = rqeptr->rqCgi.IsCliDcl;
   *vecptr++ = rqeptr->rqCgi.OutputMode;
   switch (rqeptr->rqCgi.OutputMode)
   {
      case CGI_OUTPUT_MODE_STREAM : *vecptr++ = "STREAM"; break;
      case CGI_OUTPUT_MODE_RECORD : *vecptr++ = "RECORD"; break;
      case CGI_OUTPUT_MODE_CRLF : *vecptr++ = "CRLF"; break;
      default : *vecptr++ = "?";
   }
   *vecptr++ = rqeptr->rqCgi.ProcessingBody;
   *vecptr++ = rqeptr->rqCgi.RecordCount;
   *vecptr++ = rqeptr->rqCgi.ScriptRetryCount;
   *vecptr++ = rqeptr->rqCgi.XVMSRecordMode;

   FaoVectorCheck (sizeof(FaoVector), &FaoVector, vecptr, FI_LI);

   status = WriteFaol (Buffer, sizeof(Buffer), &Length, TimerFao, &FaoVector);
   if (VMSnok (status) || status == SS$_BUFFEROVF)
      ErrorNoticed (status, "WriteFaol()", FI_LI);
   NetWrite (rqptr, 0, Buffer, Length);

   if ((cptr = rqeptr->rqCgi.BufferPtr))
   {
      /*****************/
      /* CGI variables */
      /*****************/

      for (;;)
      {
         if (!(Length = *(short*)cptr)) break;
         status = WriteFao (Buffer, sizeof(Buffer), &Length, "!&Z\n",
                            cptr + sizeof(short));
         if (VMSnok (status) || status == SS$_BUFFEROVF)
            ErrorNoticed (status, "WriteFaol()", FI_LI);
         NetWrite (rqptr, 0, Buffer, Length);
         cptr += *(short*)cptr + sizeof(short);
      }
   }

   vecptr = FaoVector;
   *vecptr++ = rqeptr->rqCache.EntryPtr;
   *vecptr++ = rqeptr->DclTaskPtr;

   status = WriteFaol (Buffer, sizeof(Buffer), &Length, CacheFao, &FaoVector);
   if (VMSnok (status) || status == SS$_BUFFEROVF)
      ErrorNoticed (status, "WriteFaol()", FI_LI);
   NetWrite (rqptr, 0, Buffer, Length);

   if (rqeptr->DclTaskPtr)
   {
      /************/
      /* DCL task */
      /************/

      vecptr = FaoVector;
      *vecptr++ = rqeptr->DclTaskPtr->TaskType;
      switch (rqeptr->DclTaskPtr->TaskType)
      {
         case DCL_TASK_TYPE_NONE : *vecptr++ = "none"; break;
         case DCL_TASK_TYPE_CLI : *vecptr++ = "CLI"; break;
         case DCL_TASK_TYPE_CGI_SCRIPT : *vecptr++ = "CGI"; break;
         case DCL_TASK_TYPE_CGIPLUS_SCRIPT : *vecptr++ = "CGIplus"; break;
         case DCL_TASK_TYPE_RTE_SCRIPT : *vecptr++ = "RTE"; break;
         default : *vecptr++ = "?";
      }
      *vecptr++ = rqeptr->DclTaskPtr->ScriptProcessPid;
      if (rqeptr->DclTaskPtr->LifeTimeSecond == DCL_DO_NOT_DISTURB)
         *vecptr++ = "DO-NOT-DISTURB";
      else
      {
         *vecptr++ = "!UL";
         *vecptr++ = rqeptr->DclTaskPtr->LifeTimeSecond;
      }
      *vecptr++ = rqeptr->DclTaskPtr->CrePrcTermMbxChannel;
      *vecptr++ = rqeptr->DclTaskPtr->CrePrcTermMbxDevName+1;
      *vecptr++ = rqeptr->DclTaskPtr->CrePrcDetachProcess;
      *vecptr++ = rqeptr->DclTaskPtr->CrePrcDetachStarting;
      *vecptr++ = rqeptr->DclTaskPtr->CrePrcUserName;
      *vecptr++ = rqeptr->DclTaskPtr->CgiPlusVarStruct;
      *vecptr++ = rqeptr->DclTaskPtr->CgiPlusInChannel;
      *vecptr++ = rqeptr->DclTaskPtr->CgiPlusInDevName+1;
      *vecptr++ = rqeptr->DclTaskPtr->CgiPlusInIOsb.Status;
      *vecptr++ = rqeptr->DclTaskPtr->CgiPlusInIOsb.Count;
      *vecptr++ = rqeptr->DclTaskPtr->QueuedCgiPlusIn;
      *vecptr++ = rqeptr->DclTaskPtr->HttpInputChannel;
      *vecptr++ = rqeptr->DclTaskPtr->HttpInputDevName+1;
      *vecptr++ = rqeptr->DclTaskPtr->HttpInputIOsb.Status;
      *vecptr++ = rqeptr->DclTaskPtr->HttpInputIOsb.Count;
      *vecptr++ = rqeptr->DclTaskPtr->QueuedHttpInput;
      *vecptr++ = rqeptr->DclTaskPtr->ClientReadBufferSize;
      *vecptr++ = rqeptr->DclTaskPtr->ClientReadStripCrLf;
      *vecptr++ = rqeptr->DclTaskPtr->QueuedClientRead;
      *vecptr++ = rqeptr->DclTaskPtr->SysCommandChannel;
      *vecptr++ = rqeptr->DclTaskPtr->SysCommandDevName+1;
      *vecptr++ = rqeptr->DclTaskPtr->SysCommandIOsb.Status;
      *vecptr++ = rqeptr->DclTaskPtr->SysCommandIOsb.Count;
      *vecptr++ = rqeptr->DclTaskPtr->QueuedSysCommand;
      *vecptr++ = rqeptr->DclTaskPtr->QueuedSysCommandAllowed;
      *vecptr++ = rqeptr->DclTaskPtr->SysOutputSize;
      *vecptr++ = rqeptr->DclTaskPtr->SysOutputChannel;
      *vecptr++ = rqeptr->DclTaskPtr->SysOutputDevName+1;
      *vecptr++ = rqeptr->DclTaskPtr->SysOutputIOsb.Status;
      *vecptr++ = rqeptr->DclTaskPtr->SysOutputIOsb.Count;
      *vecptr++ = rqeptr->DclTaskPtr->QueuedSysOutput;
      *vecptr++ = rqeptr->DclTaskPtr->BuildRecords;
      *vecptr++ = rqeptr->DclTaskPtr->SysOutputBuildCount;
      *vecptr++ = rqeptr->DclTaskPtr->ClientWriteErrorCount;
      *vecptr++ = rqeptr->DclTaskPtr->ScriptProcessPid;
      *vecptr++ = rqeptr->DclTaskPtr->CrePrcTermRecord.acc$l_finalsts;
      *vecptr++ = rqeptr->DclTaskPtr->ScriptProcessActivated;
      *vecptr++ = rqeptr->DclTaskPtr->ScriptProcessResponded;
      *vecptr++ = rqeptr->DclTaskPtr->TaskRunDown;
      *vecptr++ = rqeptr->DclTaskPtr->DeleteProcess;
      *vecptr++ = rqeptr->DclTaskPtr->ForceImageExit;
      *vecptr++ = rqeptr->DclTaskPtr->ForceImageExitGetJpi;
      *vecptr++ = rqeptr->DclTaskPtr->ForceImageExitIssued;
      *vecptr++ = rqeptr->DclTaskPtr->ForceImageExitSecond;
      *vecptr++ = rqeptr->DclTaskPtr->JpiImagNameIOsb.Status;
      *vecptr++ = rqeptr->DclTaskPtr->JpiImagNameLength;
      *vecptr++ = rqeptr->DclTaskPtr->JpiImagName;
      *vecptr++ = rqeptr->DclTaskPtr->ScriptCpuMax;
      *vecptr++ = rqeptr->DclTaskPtr->ScriptCpuTimGetJpi;
      *vecptr++ = rqeptr->DclTaskPtr->JpiCpuTimIOsb.Status;
      *vecptr++ = rqeptr->DclTaskPtr->JpiCpuTim;
      *vecptr++ = rqeptr->DclTaskPtr->ScriptCpuTimMax;
      *vecptr++ = rqeptr->DclTaskPtr->CalloutFunction;
      *vecptr++ = rqeptr->DclTaskPtr->DclCommandPtr;
      *vecptr++ = rqeptr->DclTaskPtr->DclCommandSize;
      *vecptr++ = rqeptr->DclTaskPtr->DclCommandLength;
      *vecptr++ = rqeptr->DclTaskPtr->ScriptName;
      *vecptr++ = rqeptr->DclTaskPtr->ScriptFileName;

      status = WriteFaol (Buffer, sizeof(Buffer), &Length,
                          DclTaskFao, &FaoVector);
      if (VMSnok (status) || status == SS$_BUFFEROVF)
         ErrorNoticed (status, "WriteFaol()", FI_LI);
      NetWrite (rqptr, 0, Buffer, Length);
   }

   vecptr = FaoVector;
   *vecptr++ = rqeptr->DECnetTaskPtr;

   status = WriteFaol (Buffer, sizeof(Buffer), &Length, DECnetFao, &FaoVector);
   if (VMSnok (status) || status == SS$_BUFFEROVF)
      ErrorNoticed (status, "WriteFaol()", FI_LI);
   NetWrite (rqptr, 0, Buffer, Length);

   if (rqeptr->DECnetTaskPtr)
   {
      /***************/
      /* DECnet task */
      /***************/

      vecptr = FaoVector;
      *vecptr++ = rqeptr->DECnetTaskPtr->DECnetChannel;
      *vecptr++ = rqeptr->DECnetTaskPtr->DECnetConnectIOsb.Status;
      *vecptr++ = rqeptr->DECnetTaskPtr->DECnetConnectIOsb.Count;
      *vecptr++ = rqeptr->DECnetTaskPtr->DECnetReadIOsb.Status;
      *vecptr++ = rqeptr->DECnetTaskPtr->DECnetReadIOsb.Count;
      *vecptr++ = rqeptr->DECnetTaskPtr->DECnetWriteIOsb.Status;
      *vecptr++ = rqeptr->DECnetTaskPtr->DECnetWriteIOsb.Count;
      *vecptr++ = rqeptr->DECnetTaskPtr->QueuedDECnetIO;
      *vecptr++ = rqeptr->DECnetTaskPtr->ScriptResponded;
      *vecptr++ = rqeptr->DECnetTaskPtr->BuildRecords;
      *vecptr++ = rqeptr->DECnetTaskPtr->BuildCount;
      *vecptr++ = rqeptr->DECnetTaskPtr->CgiDialogState;
      *vecptr++ = rqeptr->DECnetTaskPtr->OsuDialogState;

      status = WriteFaol (Buffer, sizeof(Buffer), &Length,
                          DECnetTaskFao, &FaoVector);
      if (VMSnok (status) || status == SS$_BUFFEROVF)
         ErrorNoticed (status, "WriteFaol()", FI_LI);
      NetWrite (rqptr, 0, Buffer, Length);
   }

   vecptr = FaoVector;
   *vecptr++ = rqeptr->DescrTaskPtr;
   *vecptr++ = rqeptr->DirTaskPtr;
   *vecptr++ = rqeptr->FileTaskPtr;
   *vecptr++ = rqeptr->GraphTaskPtr;
   *vecptr++ = rqeptr->HTAdminTaskPtr;
   *vecptr++ = rqeptr->IsMapTaskPtr;
   *vecptr++ = rqeptr->MenuTaskPtr;
   *vecptr++ = rqeptr->ProxyTaskPtr;

   status = WriteFaol (Buffer, sizeof(Buffer), &Length, DescrFao, &FaoVector);
   if (VMSnok (status) || status == SS$_BUFFEROVF)
      ErrorNoticed (status, "WriteFaol()", FI_LI);
   NetWrite (rqptr, 0, Buffer, Length);

   if (rqeptr->ProxyTaskPtr)
   {
      /**************/
      /* proxy task */
      /**************/

      NetGetBgDevice (rqeptr->ProxyTaskPtr->ProxyChannel,
                      ProxyDevName, sizeof(ProxyDevName));

      vecptr = FaoVector;
      *vecptr++ = rqeptr->ProxyTaskPtr->ProxyChannel;
      *vecptr++ = ProxyDevName+1;
      *vecptr++ = rqeptr->ProxyTaskPtr->RequestHostPort;
      *vecptr++ = rqeptr->ProxyTaskPtr->RequestSchemeName;
      *vecptr++ = rqeptr->ProxyTaskPtr->ProxyLookupRetryCount;
      *vecptr++ = rqeptr->ProxyTaskPtr->ProxyConnectIOsb.Status;
      *vecptr++ = rqeptr->ProxyTaskPtr->ProxyConnectIOsb.Count;
      *vecptr++ = rqeptr->ProxyTaskPtr->ProxyReadIOsb.Status;
      *vecptr++ = rqeptr->ProxyTaskPtr->ProxyReadIOsb.Count;
      *vecptr++ = rqeptr->ProxyTaskPtr->ProxyReadRawAstFunction;
      *vecptr++ = rqeptr->ProxyTaskPtr->ProxyWriteIOsb.Status;
      *vecptr++ = rqeptr->ProxyTaskPtr->ProxyWriteIOsb.Count;
      *vecptr++ = rqeptr->ProxyTaskPtr->ProxyWriteRawAstFunction;
      *vecptr++ = rqeptr->ProxyTaskPtr->BytesRawRx;
      *vecptr++ = rqeptr->ProxyTaskPtr->BytesRawTx;
      *vecptr++ = rqeptr->ProxyTaskPtr->VerifyRecordPtr;
      *vecptr++ = rqeptr->ProxyTaskPtr->RebuiltRequestPtr;
      *vecptr++ = rqeptr->ProxyTaskPtr->ResponseConsecutiveNewLineCount;
      *vecptr++ = rqeptr->ProxyTaskPtr->ResponseHeaderLength;
      if (!rqeptr->ProxyTaskPtr->ResponseHeaderPtr)
      {
         *vecptr++ = sizeof(WATCH_NULL_STRING)-1;
         *vecptr++ = WATCH_NULL_STRING;
      }
      else
      if (rqeptr->ProxyTaskPtr->ResponseConsecutiveNewLineCount < 2)
      {
         *vecptr++ = rqeptr->ProxyTaskPtr->ResponseHeaderLength;
         *vecptr++ = WATCH_NULL(rqeptr->ProxyTaskPtr->ResponseHeaderPtr);
      }
      else
      {
         *vecptr++ = sizeof(WATCH_NULL_STRING)-1;
         *vecptr++ = WATCH_NULL_STRING;
      }
      *vecptr++ = rqeptr->ProxyTaskPtr->ResponseContentLength;
      *vecptr++ = rqeptr->ProxyTaskPtr->ResponseBodyLength;
      *vecptr++ = rqeptr->ProxyTaskPtr->ResponseBufferNetCount;
      *vecptr++ = rqeptr->ProxyTaskPtr->ResponseBufferNetPtr;
      *vecptr++ = rqeptr->ProxyTaskPtr->ResponseBufferCacheCount;
      *vecptr++ = rqeptr->ProxyTaskPtr->ResponseBufferCachePtr;
      *vecptr++ = rqeptr->ProxyTaskPtr->NotCacheable;
      *vecptr++ = rqeptr->ProxyTaskPtr->ProxyCacheFileName;
      *vecptr++ = rqeptr->ProxyTaskPtr->ProxyCacheSuitable;
      *vecptr++ = rqeptr->ProxyTaskPtr->ProxyCacheFileSizeInBytes;
      *vecptr++ = rqeptr->ProxyTaskPtr->ProxyCacheLastModifiedHours;
      *vecptr++ = rqeptr->ProxyTaskPtr->ProxyCacheReadBytes;
      *vecptr++ = rqeptr->ProxyTaskPtr->ProxyRequestNumber;
      *vecptr++ = rqeptr->ProxyTaskPtr->ProxyCacheFileCdt;

      status = WriteFaol (Buffer, sizeof(Buffer), &Length,
                          ProxyTaskFao, &FaoVector);
      if (VMSnok (status) || status == SS$_BUFFEROVF)
         ErrorNoticed (status, "WriteFaol()", FI_LI);
      NetWrite (rqptr, 0, Buffer, Length);
   }

   vecptr = FaoVector;
   *vecptr++ = rqeptr->PutTaskPtr;
   *vecptr++ = rqeptr->SsiTaskPtr;
   *vecptr++ = rqeptr->UpdTaskPtr;

   status = WriteFaol (Buffer, sizeof(Buffer), &Length, PutFao, &FaoVector);
   if (VMSnok (status) || status == SS$_BUFFEROVF)
      ErrorNoticed (status, "WriteFaol()", FI_LI);
   NetWrite (rqptr, 0, Buffer, Length);

   Watch.Module = WatchModule;
}

/*****************************************************************************/
/*
Display the size of selected data structures.
*/ 

WatchReportStruct
(
REQUEST_STRUCT *rqptr,
REQUEST_AST NextTaskFunction
)
{
#if WATCH_MOD

   static char  ResponseFao [] =
"!25<AccountingStruct!> !6UL\n\
!25<ActivityGblSecStruct!> !6UL\n\
!25<AuthCacheRecordStruct!> !6UL\n\
!25<AuthConfigStruct!> !6UL\n\
!25<AuthHtaRecordStruct!> !6UL\n\
!25<AuthPathRecordStruct!> !6UL\n\
!25<AuthRealmRecordStruct!> !6UL\n\
!25<CacheStruct!> !6UL\n\
!25<DclTaskStruct!> !6UL\n\
!25<DclScriptNameCacheStruct!> !6UL\n\
!25<DirTaskStruct!> !6UL\n\
!25<DECnetConnectStruct!> !6UL\n\
!25<DECnetTaskStruct!> !6UL\n\
!25<HttpdGblSecStruct!> !6UL\n\
!25<MonRequestStruct!> !6UL\n\
!25<OdsStruct!> !6UL\n\
!25<ProxyTaskStruct!> !6UL\n\
!25<ProxyAccountingStruct!> !6UL\n\
!25<PutTaskStruct!> !6UL\n\
!25<RequestStruct!> !6UL\n\
!25<ServiceStruct!> !6UL\n\
!25<SsiStruct!> !6UL\n\
!25<UpdTaskStruct!> !6UL\n";

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

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

   if (Debug) fprintf (stdout, "WatchReportStruct()\n");

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

   RESPONSE_HEADER_200_PLAIN (rqptr);

   vecptr = FaoVector;
   *vecptr++ = sizeof(struct AccountingStruct);
   *vecptr++ = sizeof(struct ActivityGblSecStruct);
   *vecptr++ = sizeof(struct AuthCacheRecordStruct);
   *vecptr++ = sizeof(struct AuthConfigStruct);
   *vecptr++ = sizeof(struct AuthHtaRecordStruct);
   *vecptr++ = sizeof(struct AuthPathRecordStruct);
   *vecptr++ = sizeof(struct AuthRealmRecordStruct);
   *vecptr++ = sizeof(struct CacheStruct);
   *vecptr++ = sizeof(struct DclTaskStruct);
   *vecptr++ = sizeof(struct DclScriptNameCacheStruct);
   *vecptr++ = sizeof(struct DirTaskStruct);
   *vecptr++ = sizeof(struct DECnetConnectStruct);
   *vecptr++ = sizeof(struct DECnetTaskStruct);
   *vecptr++ = sizeof(HTTPD_GBLSEC);
   *vecptr++ = sizeof(struct MonRequestStruct);
   *vecptr++ = sizeof(struct OdsStruct);
   *vecptr++ = sizeof(struct ProxyTaskStruct);
   *vecptr++ = sizeof(PROXY_ACCOUNTING_STRUCT);
   *vecptr++ = sizeof(struct PutTaskStruct);
   *vecptr++ = sizeof(struct RequestStruct);
   *vecptr++ = sizeof(struct ServiceStruct);
   *vecptr++ = sizeof(struct SsiTaskStruct);
   *vecptr++ = sizeof(struct UpdTaskStruct);

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

   SysDclAst (NextTaskFunction, rqptr);

#else /* WATCH_MOD */

   rqptr->rqResponse.HttpStatus = 403;
   ErrorGeneral (rqptr, ErrorWatchNoModule, FI_LI);
   SysDclAst (NextTaskFunction, rqptr);

#endif /* WATCH_MOD */
}

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

