/*****************************************************************************/ /* Control.c This module implements the HTTPd command-line, and the distributed, to-all server processes (from command-line or Admin Menu), control functionality. Command-line server control commands: o ALIGN= START, STOP, ZERO and [,,] o AUTH reload authorization file o AUTH=CHECK elementary check of authorization file o AUTH=LOAD reload authorization file o AUTH=PURGE purge authentication records from cache o AUTH=SKELKEY= see AUTH.C for syntax and usage o CACHE=ON turn file caching on o CACHE=OFF turn file caching off o CACHE=PURGE purge all data cached O CONFIG=CHECK elementary check of all configuration files o DCL=DELETE unconditionally delete all DCL script processes (see below for additional parameters) o DCL=PURGE delete idle script processes, mark busy for later deletion (see below for additional parameters) o DECNET=PURGE disconnect idle DECnet script tasks o DECNET=DISCONNECT forceably disconnect all DECnet script tasks o DISCONNECT idle network connections o DISCONNECT=integer this connection number o DISCONNECT=ALL all network connection o DISCONNECT=URI=pattern network connection with matching request URI o EXIT exit after all client activity complete (nothing new started) o EXIT=NOW exit right now regardless of connections o GLOBAL=CHECK elementary check of the global configuration file o INSTANCE=MAX|CPU|integer explicitly set the startup instance value o INSTANCE=ACTIVE|STANDBY move between active and standby mode o LIST *special-case* when used with /ALL just list all servers o LOG=OPEN open the log file(s) o *LOG=OPEN=name *OBSOLETE* open the log file using the specified name o LOG=REOPEN closes then reopens the log o *LOG=REOPEN=name *OBSOLETE* log reopened using specified name o LOG=CLOSE close the log file(s) o LOG=FLUSH flush the log file(s) o *LOG=FORMAT=string *OBSOLETE* set the log format o *LOG=PERIOD=string *OBSOLETE* set the log period o MAP reload mapping rule file o MAP=CHECK elementary check of the mapping rule file o MAP=LOAD reload mapping rule file o MSG=CHECK elementary check of the message file o NET=PURGE purge persistent (idle) connections o NET=PURGE=integer purge this particular connection number o NET=PURGE=ALL purge all (idle and non-idle) connections o NET=PURGE=URI=pattern purge connections with matching request URI o NET=RESUME resume accepting connections (see suspend) o NET=SUSPEND stop accepting connections (see resume) o NET=SUSPEND=NOW as for suspend and terminate those in-progress o NOTE=string provide an admin note to the metacon rules o PROXY=ON proxy processing enabled o PROXY=OFF proxy processing disabled o PROXY=PURGE=BACKGROUND background cache purge o PROXY=PURGE=REACTIVE reactive cache purge o PROXY=PURGE=ROUTINE routine cache purge o PROXY=PURGE=HOST host name cache purge o PROXY=STATISTICS perform statistics scan of cache o PROXY=STOP=SCAN stop an in-progress purge or statistics scan o RESTART effectively exit server image then re-activate o RESTART=NOW restart NOW regardless of connections o RESTART=QUIET restart if-and-when current requests reach zero o SERVICE=CHECK elementary check of service file o SSL=CA=LOAD reload the CA verification file o *SSL=KEY=PASSWORD *special case* prompt/supply private key password o THROTTLE=RELEASE release all queued requests for processing o THROTTLE=TERMINATE terminate all queued requests o THROTTLE=ZERO zero throttle statistics o WEBSOCKET=DISCONNECT all WebSocket connections o WEBSOCKET=DISCONNECT=integer this WebSocket connection number o WEBSOCKET=DISCONNECT=SCRIPT=pattern matching this script name o WEBSOCKET=DISCONNECT=USER=pattern matching this scripting user name o ZERO=NOTICED zero the 'errors noticed' accounting o ZERO=PROXY zero proxy accounting o ZERO zero all accounting These commands are entered at the DCL command line (interpreted in the CLI.c module) in the following manner. $ HTTPD /DO=command If /ALL is added then the command is applied to all HTTPd server processes on the node or cluster (asuming there is more than one executing, requires SYSLCK). Due to architectural constraints, those commands marked "*" in the above list cannot be used with the /ALL qualifier. $ HTTPD /DO=command /ALL For example: $ HTTPD /DO=EXIT the server exits if when clients connected $ HTTPD /DO=EXIT=NOW the server exists immediately $ HTTPD /DO=RESTART server exits and then restarts $ HTTPD /DO=RESTART/ALL all servers on node/cluster exit and restarts $ HTTPD /DO=DCL=PURGE delete all persistent DCL script processes $ HTTPD /DO=LOG=CLOSE close the log file $ HTTPD /DO=ZERO/ALL zero the accounting on all servers $ HTTPD /DO=LIST/ALL *special-case*, just list all servers Single node and cluster directives are implemented using cluster-wide locking resource names and shared memory. For single server /DO= control the lock-resource name serves to notify (all) server(s) that a directive has been written into the global section shared memory control buffer. All participating servers check this area, only the one with a current directive written into its shared-memory control buffer performs it. For multiple server /DO=/ALL control, the lock value block contains the actual directive and so participating servers act on that alone. The originating image requests a cluster-wide lock before originating a directive, effectively locking others out during the process (which is most commonly a very short period). Servers could potentially be grouped using the resource names. The /ALL qualifier accepts an optional string that will become part of the lock resource name. Hence when servers belonging to a particular group are started up the startup procedure would include something like "/ALL=1", and then Admin Menu directives would apply to only those in that group, and command-line directives would be used "/DO=command/ALL=1", etc. Note that the /ALL= parameter can be any alphanumeric string up to 8 characters. DCL PURGE/DELETE PARAMETERS --------------------------- On non-VAX systems under VMS V8.2 or later (and supported by the 64 byte lock value block) is is possible to supply further parameters refining the scope of the PURGE/DELETE. The following directive limits the PURGE/DELETE to script processes executing under the specified account. HTTPD/DO=DCL=PURGE=USER= HTTPD/DO=DCL=DELETE=USER= This variant acts only on script processes with script paths matching the specified pattern. HTTPD/DO=DCL=PURGE=SCRIPT= HTTPD/DO=DCL=DELETE=SCRIPT= This variant acts only on script processes with script VMS file specifications matching the specified pattern. HTTPD/DO=DCL=PURGE=FILE= HTTPD/DO=DCL=DELETE=FILE= These cannot be combined. THROTTLE RELEASE/TERMINATE PARAMETERS ------------------------------------- In a similar fashion to the DCL= parameters, on non-VAX systems under VMS V8.2 or later is is possible to supply further parameters refining the scope of the RELEASE/TERMINATE. The following directive limits the PURGE/DELETE to script processes executing under the specified account. HTTPD/DO=THROTTLE=RELEASE=USER= HTTPD/DO=THROTTLE=TERMINATE=USER= This variant acts only on script processes with script paths matching the specified pattern. HTTPD/DO=THROTTLE=RELEASE=SCRIPT= HTTPD/DO=THROTTLE=TERMINATE=SCRIPT= These cannot be combined. SERVER CONTROL LOCK ------------------- When a server starts up it uses InstanceLockNotifySet() to enqueue a CR (concurrent read) lock against the resource name using ControlHttpdLock(). This lock allows a "blocking" AST to be delivered (back to ControlHttpdLock()) indicating a CLI-command or other server is wishing to initiate a control action. It releases that original CR lock then immediately enqueues another CR so that it can read the lock value block subsequently written to by the initiator's now-granted EX mode lock (see immediately below). For VAX and pre-V8.2 Alpha and Itanium the lock value block will contain a maximum 15 character, null-terminated string (the 16 bytes) with the directive. For non-VAX V8.2 and later the lock value block has a maximum size of 64 bytes, allowing a 63 character string. The AST delivery will provide this value block to ControlHttpdAst() which calls the appropriate function to perform or ignore the directive. INITIATOR CONTROL LOCK ---------------------- To initiate some control activity on one or more servers the initiator (either at the command-line or from the Admin Menu) enqueues a CR (concurrent read) lock on the appropriate resource name. It then converts that lock to EX (exclusive). Those with original CR locks enqueued have a "blocking" AST delivered, release their locks allowing the initiator to obtain the requested EX lock. It then, using a sys$deq(), writes the command string into the lock value block. When the exclusive lock is dequeued the servers waiting on the subsequent CR locks have them delivered, allowing them to read the value block and they can then check the lock status block for a directive (which they may or may not act upon depending on the contents). VERSION HISTORY --------------- 15-OCT-2011 MGD /DO=NET=PURGE[=..] expanded /DO=WEBSOCKET=DISCONNECT[=..] 12-MAR-2011 MGD /DO=ALIGN=.. 14-JUL-2010 MGD command-line checks of configuration files /DO=AUTH=CHECK /DO=CONFIG=CHECK (all configuration files) /DO=GLOBAL=CHECK /DO=MAP=CHECK /DO=MSG=CHECK /DO=SERVICE=CHECK 01-JUL-2010 MGD Mapurl_ControlReload() rather than Mapurl_Load() 15-JUL-2006 MGD DO=INSTANCE=ACTIVE|STANDBY DO=NET=PURGE[=NOW]|SUSPEND[=NOW]|RESUME 20-JUL-2005 MGD DO=ZERO=NOTICED to reset 'errors noticed' accounting 25-MAY-2005 MGD allow for VMS V8.2 64 byte lksb$b_valblk NOTE= to provide note to metacon rules 30-APR-2005 MGD AUTH=SKELKEY period is allowed to be zero 05-DEC-2004 MGD ControlHttpdAst() CONTROL_ZERO_PROXY 30-NOV-2004 MGD ControlZeroProxyAccounting() 31-DEC-2003 MGD ControlDelay() if multiple instances add a further, random delay to prevent mass suicide with reduced instances and a RESTART=NOW directive 06-JAN-2003 MGD bugfix; ControlEnqueueCommand() occasional race condition between InstanceLockList() and InstanceLockNotifyNow() AST disabling SYSLCK during list (happy birthday Naomi) 07-DEC-2002 MGD skeleton-key authentication 30-MAY-2002 MGD RESTART=QUIET restart when(-and-if) requests reach zero 21-MAY-2002 MGD ControlZeroAccounting() under instance supervisor control 04-APR-2002 MGD proxy maintenance STOP scan 29-SEP-2001 MGD significant changes in support of per-node instances 04-AUG-2001 MGD support module WATCHing 30-JUN-2001 MGD bugfix; missing log open code, strlen(.._AS), obsolete "log=open=" and "log=reopen=" 18-MAY-2001 MGD global section shared memory and DLM for control, provide SSL private key password directive 06-APR-2001 MGD allow WATCH to note control events 13-MAR-2001 MGD add THROTTLE=... 28-DEC-2000 MGD add SSL=CA=LOAD 03-DEC-2000 MGD bugfix; CachePurge() 18-JUN-2000 MGD add node/cluster-wide, sys$enq()-driven directives (/ALL) 12-JAN-2000 MGD rework control reporting, adding OPCOM messages 20-JAN-1999 MGD proxy serving controls 15-AUG-1998 MGD decnet=purge, decnet=disconnect 16-MAR-1998 MGD restart=now 05-OCT-1997 MGD cache and logging period control 28-SEP-1997 MGD reinstated some convenience commands (AUTH, DCL, and ZERO) 01-FEB-1997 MGD HTTPd version 4 (removed much of its previous functionality) 01-OCT-1996 MGD minor changes to authorization do commands 01-JUL-1996 MGD controls for path/realm-based authorization/authentication 01-DEC-1995 MGD HTTPd version 3 12-APR-1995 MGD support logging 03-APR-1995 MGD support authorization 20-DEC-1994 MGD initial development for multi-threaded version of HTTPd */ /*****************************************************************************/ #ifdef WASD_VMS_V7 #undef _VMS__V6__SOURCE #define _VMS__V6__SOURCE #undef __VMS_VER #define __VMS_VER 70000000 #undef __CRTL_VER #define __CRTL_VER 70000000 #endif /* standard C header files */ #include #include #include #include /* VMS related header files */ #include #include #include #include #include #include #include #include #include /* application-related header files */ #include "wasd.h" #define WASD_MODULE "CONTROL" /******************/ /* global storage */ /******************/ BOOL ControlDoAllHttpd, ControlExitRequested, ControlRestartQuiet, ControlRestartRequested; unsigned long ControlPid; char *ControlCommandPtr; char ControlBuffer [256], ControlNodeName [16], ControlProcessName [16], ControlUserName [13]; /********************/ /* external storage */ /********************/ extern BOOL CacheEnabled, HttpdServerExecuting, HttpdTicking, InstanceNodeSupervisor, OperateWithSysPrv, ProxyCacheFreeSpaceAvailable, ProxyServingEnabled; extern int CliServerPort, EfnWait, ExitStatus, HttpdGblSecPages, HttpdGblSecVersion, HttpdTickSecond, InstanceEnvNumber, InstanceNodeConfig, InstanceNodeCurrent, NetConnectProcessing, OpcomMessages, ServerPort; extern unsigned short HttpdNumTime[]; extern unsigned long GblSecPrvMask[], SysLckMask[], SysPrvMask[], WorldMask[]; extern char ErrorSanityCheck[], ErrorXvalNotValid[], LoggingFileName[], LoggingPeriodName[], SoftwareID[]; extern INSTANCE_LOCK InstanceLockTable[]; extern ACCOUNTING_STRUCT *AccountingPtr; extern HTTPD_GBLSEC *HttpdGblSecPtr; extern PROXY_ACCOUNTING_STRUCT *ProxyAccountingPtr; extern SYS_INFO SysInfo; extern WATCH_STRUCT Watch; /*****************************************************************************/ /* */ ControlInit () { int status; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD__OTHER)) WatchThis (NULL, FI_LI, WATCH_MOD__OTHER, "ControlInit()"); InstanceLockNotifySet (INSTANCE_NODE_DO, &ControlHttpdAst); InstanceLockNotifySet (INSTANCE_CLUSTER_DO, &ControlHttpdAst); } /*****************************************************************************/ /* This is the primary function called when a /DO= is made at the command line. This function attempts to map the appropriate global section to access memory shared with the server. It then call the appropriate function to request the server perform the directive, either locally or if /ALL was used on the CLI across all servers in the group (cluster). */ int ControlCommand (char *Command) { /* system global section, map into first available virtual address */ static int MapFlags = SEC$M_SYSGBL | SEC$M_EXPREG | SEC$M_WRT; /* it is recommended to map into any virtual address in the region (P0) */ static unsigned long InAddr [2] = { 0x200, 0x200 }; int cnt, status, LockIndex, ByteSize, PageSize, PeriodMinutes; unsigned short ShortLength; unsigned long RetAddr [2]; char *cptr, *sptr, *zptr; char GblSecName [32], PrivateKeyPasswd [64]; META_CONFIG *mcptr; $DESCRIPTOR (GblSecNameDsc, GblSecName); /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD__OTHER)) WatchThis (NULL, FI_LI, WATCH_MOD__OTHER, "ControlCommand()"); if (strsame (Command, CONTROL_CONFIG_CHECK, -1)) { status = ConfigLoad (&mcptr); cnt = ControlCheck (mcptr, "global", status); status = ServiceConfigLoad (&mcptr); cnt += ControlCheck (mcptr, "service", status); status = MapUrl_ConfigLoad (&mcptr); cnt += ControlCheck (mcptr, "map", status); status = AuthConfigLoad (&mcptr); cnt += ControlCheck (mcptr, "auth", status); status = MsgConfigLoad (&mcptr); cnt += ControlCheck (mcptr, "msg", status); if (cnt) FaoToStdout ("%HTTPD-F-CHECK, !UL fatal error!%s\n", cnt); else FaoToStdout ("%HTTPD-I-CHECK, 0 fatal errors \ (does not mean it will work as intended!!)\n"); return (SS$_NORMAL); } if (strsame (Command, CONTROL_AUTH_CHECK, -1)) { status = AuthConfigLoad (&mcptr); ControlCheck (mcptr, "auth", status); return (SS$_NORMAL); } if (strsame (Command, CONTROL_GLOBAL_CHECK, -1)) { status = ConfigLoad (&mcptr); ControlCheck (mcptr, "global", status); return (SS$_NORMAL); } if (strsame (Command, CONTROL_MAP_CHECK, -1)) { status = MapUrl_ConfigLoad (&mcptr); ControlCheck (mcptr, "map", status); return (SS$_NORMAL); } if (strsame (Command, CONTROL_MSG_CHECK, -1)) { status = MsgConfigLoad (&mcptr); ControlCheck (mcptr, "msg", status); return (SS$_NORMAL); } if (strsame (Command, CONTROL_SERVICE_CHECK, -1)) { status = ServiceConfigLoad (&mcptr); ControlCheck (mcptr, "service", status); return (SS$_NORMAL); } if (CliServerPort) ServerPort = CliServerPort; if (ServerPort < 1 || ServerPort > 65535) { FaoToStdout ("%HTTPD-E-PORT, IP port out-of-range\n"); exit (STS$K_ERROR | STS$M_INHIB_MSG); } FaoToBuffer (GblSecName, sizeof(GblSecName), &ShortLength, GBLSEC_NAME_FAO, HTTPD_NAME, HTTPD_GBLSEC_VERSION_NUMBER, InstanceEnvNumber, "HTTPD"); GblSecNameDsc.dsc$w_length = ShortLength; /* map the specified global section */ sys$setprv (1, &GblSecPrvMask, 0, 0); status = sys$mgblsc (&InAddr, &RetAddr, 0, MapFlags, &GblSecNameDsc, 0, 0); sys$setprv (0, &GblSecPrvMask, 0, 0); if (VMSok (status)) { ByteSize = (RetAddr[1]+1) - RetAddr[0]; PageSize = (RetAddr[1]+1) - RetAddr[0] >> 9; HttpdGblSecPtr = (HTTPD_GBLSEC*)RetAddr[0]; HttpdGblSecPages = PageSize; } else { if (status == SS$_NOSUCHSEC) { FaoToStdout ("%HTTPD-E-SERVER, no such server!!\n\ -SYSTEM-E-NOSUCHSEC, no such (global) section\n"); exit (status | STS$M_INHIB_MSG); } else exit (status); } if (HttpdGblSecPtr->GblSecVersion != HttpdGblSecVersion) FaoToStdout ("%HTTPD-W-GBLSECMIS, \ global section version mismatch (!8XL/!8XL)\n\ -GBLSECMIS-W-TRYING, will attempt directive (no guarantees!!)\n", HttpdGblSecPtr->GblSecVersion, HttpdGblSecVersion); if (strsame (Command, CONTROL_SSL_PKPASSWD, -1)) { /************************/ /* private key password */ /************************/ stdin = freopen ("SYS$INPUT", "r", stdin, "ctx=rec", "rop=rne", "rop=tmo", "tmo=60"); if (!stdin) exit (vaxc$errno); memset (PrivateKeyPasswd, 0, sizeof(PrivateKeyPasswd)); fprintf (stdout, "Enter private key password []: "); fgets (PrivateKeyPasswd, sizeof(PrivateKeyPasswd), stdin); fputc ('\n', stdout); /* ensure it's null terminated */ PrivateKeyPasswd [sizeof(PrivateKeyPasswd)-1] = '\0'; /* remove any trailing newline */ if (PrivateKeyPasswd[0]) PrivateKeyPasswd [strlen(PrivateKeyPasswd)-1] = '\0'; /* enable SYSPRV to allow writing into the global section */ sys$setprv (1, &SysPrvMask, 0, 0); zptr = (sptr = HttpdGblSecPtr->PkPasswd) + sizeof(HttpdGblSecPtr->PkPasswd)-1; for (cptr = PrivateKeyPasswd; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; sys$setprv (0, &SysPrvMask, 0, 0); } if (strsame (Command, CONTROL_AUTH_SKELKEY, sizeof(CONTROL_AUTH_SKELKEY)-1)) { /*******************************/ /* skeleton key authentication */ /*******************************/ InstanceMutexLock (INSTANCE_MUTEX_HTTPD); PeriodMinutes = -1; cptr = Command + sizeof(CONTROL_AUTH_SKELKEY)-1; if (!*cptr || *cptr == '0') PeriodMinutes = 0; else { zptr = (sptr = HttpdGblSecPtr->AuthSkelKeyUserName) + sizeof(HttpdGblSecPtr->AuthSkelKeyUserName)-1; while (*cptr && *cptr != ':' && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; if (*cptr == ':') { cptr++; zptr = (sptr = HttpdGblSecPtr->AuthSkelKeyPassword) + sizeof(HttpdGblSecPtr->AuthSkelKeyPassword)-1; while (*cptr && *cptr != ':' && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; if (HttpdGblSecPtr->AuthSkelKeyUserName[0] != '_') FaoToStdout ("%HTTPD-E-DO, _:\n"); else if (strlen(HttpdGblSecPtr->AuthSkelKeyUserName) < 7) FaoToStdout ("%HTTPD-E-DO, username too short\n"); else if (strsame (HttpdGblSecPtr->AuthSkelKeyUserName, "_SKELE", 6)) FaoToStdout ("%HTTPD-E-DO, not a good choice!!\n"); else if (sptr - HttpdGblSecPtr->AuthSkelKeyPassword < 8) FaoToStdout ("%HTTPD-E-DO, password too short\n"); else { if (strsame (HttpdGblSecPtr->AuthSkelKeyUserName, HttpdGblSecPtr->AuthSkelKeyPassword, -1) || strsame (HttpdGblSecPtr->AuthSkelKeyUserName+1, HttpdGblSecPtr->AuthSkelKeyPassword, -1)) { FaoToStdout ("%HTTPD-E-DO, not a good choice!!\n"); PeriodMinutes = -1; } else if (*cptr == ':') { cptr++; PeriodMinutes = atoi(cptr); /* less than a minute or greater than a week */ if (PeriodMinutes < 0 || PeriodMinutes > 10080) { FaoToStdout ("%HTTPD-E-DO, invalid period\n"); PeriodMinutes = -1; } } else PeriodMinutes = 60; } } else FaoToStdout ("%HTTPD-E-DO, _:\n"); } if (PeriodMinutes <= 0) { memset (HttpdGblSecPtr->AuthSkelKeyUserName, 0, sizeof(HttpdGblSecPtr->AuthSkelKeyUserName)); memset (HttpdGblSecPtr->AuthSkelKeyPassword, 0, sizeof(HttpdGblSecPtr->AuthSkelKeyPassword)); HttpdGblSecPtr->AuthSkelKeyHttpdTickSecond = 0; } else { /* ControlHttpdAst() will turn this into seconds into the future */ HttpdGblSecPtr->AuthSkelKeyHttpdTickSecond = PeriodMinutes; } InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD); /* if an error then don't enqueue the command */ if (PeriodMinutes < 0) exit (SS$_NORMAL); /* mask the sensitive information */ cptr = Command + sizeof(CONTROL_AUTH_SKELKEY)-1; if (*cptr != '0') SET2(cptr,'*\0'); /* drop through to enqueue the command */ } if (ControlDoAllHttpd) { LockIndex = INSTANCE_CLUSTER_DO; sptr = "ALL"; } else { LockIndex = INSTANCE_NODE_DO; sptr = ""; } status = InstanceLockControl (); if (status == SS$_NOTQUEUED) { FaoToStdout ("%HTTPD-E-DO!AZ, in use elsewhere!!\n", sptr); exit (status | STS$M_INHIB_MSG); } if (VMSnok (status)) exit (status); cptr = ControlEnqueueCommand (LockIndex, Command); for (cnt = CONTROL_RESPONSE_SECONDS * 5; cnt; cnt--) { /* poll the progress of the enqueue */ if (!InstanceLockNotifyNow (0, NULL)) break; ControlSleep (200); } InstanceUnLockControl (); if (!cnt) { FaoToStdout ("%HTTPD-E-DO!AZ, \ command (en)queueing did not complete within !UL seconds\n", sptr, CONTROL_RESPONSE_SECONDS); return (SS$_TIMEOUT | STS$M_INHIB_MSG); } if (*cptr == '!') FaoToStdout ("%HTTPD-E-DO!AZ, !AZ\n", sptr, cptr+1); else FaoToStdout ("%HTTPD-I-DO!AZ, !AZ\n", sptr, cptr); return (SS$_NORMAL); } /*****************************************************************************/ /* Provide output to the command-line check of the configuration file. */ int ControlCheck ( META_CONFIG *mcptr, char *config, int status ) { char *severity; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD__OTHER)) WatchThis (NULL, FI_LI, WATCH_MOD__OTHER, "ControlCheck()"); if (VMSnok (status)) { FaoToStdout ("%HTTPD-F-CHECK, !AZ config\n!AZ\n!AZ\n", config, mcptr->LoadReport.FileName, mcptr->LoadReport.TextPtr); return (1); } if (mcptr->LoadReport.ErrorCount) severity = "E"; else if (mcptr->LoadReport.WarningCount) severity = "W"; else if (mcptr->LoadReport.InformCount) severity = "I"; else severity = "S"; FaoToStdout ("%HTTPD-!AZ-CHECK, !AZ config; \ !UL informational!%s, !UL warning!%s, !UL error!%s\n!AZ\n!AZ\n", severity, config, mcptr->LoadReport.InformCount, mcptr->LoadReport.WarningCount, mcptr->LoadReport.ErrorCount, mcptr->LoadReport.FileName, mcptr->LoadReport.TextPtr); return (0); } /*****************************************************************************/ /* Check that the command is known and can be done via a /ALL or via the ADMINISTRATION MENU. Enqueues a CR (concurrent read) lock on the specified resource name, then gets the number of current locks (i.e. other processes) currently with an interest in that lock. Then converts that lock to EX (exclusive) and using a sys$deq() writes the command string into the lock value block. When the exclusive lock is removed the other processes waiting on CR locks get them, reading the value block and excuting the command therein. */ char* ControlEnqueueCommand ( int LockIndex, char *Command ) { static char String [512]; BOOL check16, dlm820; int clen, plen, status, ServerCount; char *cptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD__OTHER)) WatchThis (NULL, FI_LI, WATCH_MOD__OTHER, "ControlEnqueueCommand() !UL !&Z", LockIndex, Command); /* length of command (up to any second equate symbol) */ for (cptr = Command; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; while (*cptr && *cptr != '=') cptr++; clen = cptr - Command; /* now the length of any parameter */ if (*cptr) cptr++; while (*cptr) cptr++; plen = cptr - Command - clen; /* a synonym (because I always forget the correct command) */ if (strsame (Command, CONTROL_PROXY_PURGE_STOP, -1)) strcpy (Command, CONTROL_PROXY_STOP_SCAN); check16 = false; if (strsame (Command, CONTROL_ALIGN_FAULT, sizeof(CONTROL_ALIGN_FAULT)-1) || strsame (Command, CONTROL_AUTH_LOAD1, -1) || strsame (Command, CONTROL_AUTH_LOAD2, -1) || strsame (Command, CONTROL_AUTH_PURGE, -1) || strsame (Command, CONTROL_AUTH_SKELKEY, clen) || strsame (Command, CONTROL_CACHE_ON, -1) || strsame (Command, CONTROL_CACHE_OFF, -1) || strsame (Command, CONTROL_CACHE_PURGE, -1) || (check16 = strsame (Command, CONTROL_DCL_DELETE, clen)) || (check16 = strsame (Command, CONTROL_DCL_PURGE, clen)) || strsame (Command, CONTROL_DECNET_PURGE, -1) || strsame (Command, CONTROL_DECNET_DISCONNECT, -1) || strsame (Command, CONTROL_EXIT, -1) || strsame (Command, CONTROL_EXIT_NOW, -1) || strsame (Command, CONTROL_INSTANCE, sizeof(CONTROL_INSTANCE)-1) || strsame (Command, CONTROL_LIST, -1) || strsame (Command, CONTROL_LOG_OPEN, -1) || strsame (Command, CONTROL_LOG_CLOSE, -1) || strsame (Command, CONTROL_LOG_FLUSH, -1) || strsame (Command, CONTROL_LOG_REOPEN, -1) || strsame (Command, CONTROL_MAP_LOAD1, -1) || strsame (Command, CONTROL_MAP_LOAD2, -1) || (check16 = strsame (Command, CONTROL_NET_PURGE_URI, sizeof(CONTROL_NET_PURGE_URI)-1)) || strsame (Command, CONTROL_NET_PURGE_ALL, -1) || strsame (Command, CONTROL_NET_PURGE_NOW, -1) || strsame (Command, CONTROL_NET_PURGE, sizeof(CONTROL_NET_PURGE)-1) || strsame (Command, CONTROL_NET_RESUME, -1) || strsame (Command, CONTROL_NET_SUSPEND, -1) || strsame (Command, CONTROL_NET_SUSPEND_NOW, -1) || strsame (Command, CONTROL_NET_NOSUSPEND, -1) || (check16 = strsame (Command, CONTROL_NOTE, sizeof(CONTROL_NOTE)-1)) || strsame (Command, CONTROL_PROXY_ON, -1) || strsame (Command, CONTROL_PROXY_OFF, -1) || strsame (Command, CONTROL_PROXY_PURGE_BCKGRND, -1) || strsame (Command, CONTROL_PROXY_PURGE_REACTIVE, -1) || strsame (Command, CONTROL_PROXY_PURGE_ROUTINE, -1) || strsame (Command, CONTROL_PROXY_PURGE_HOST, -1) || strsame (Command, CONTROL_PROXY_STATISTICS, -1) || strsame (Command, CONTROL_PROXY_STOP_SCAN, -1) || strsame (Command, CONTROL_RESTART, -1) || strsame (Command, CONTROL_RESTART_NOW, -1) || strsame (Command, CONTROL_RESTART_QUIET, -1) || strsame (Command, CONTROL_SSL_CA_LOAD, -1) || (check16 = strsame (Command, CONTROL_THROTTLE_RELEASE, clen)) || (check16 = strsame (Command, CONTROL_THROTTLE_TERMINATE, clen)) || strsame (Command, CONTROL_THROTTLE_ZERO, -1) || (check16 = strsame (Command, CONTROL_WEBSOCKET_DISCONNECT, clen)) || strsame (Command, CONTROL_ZERO_NOTICED, -1) || strsame (Command, CONTROL_ZERO_PROXY, -1) || strsame (Command, CONTROL_ZERO, -1)) { if (check16 && SysInfo.LockValueBlockSize == LOCK_VALUE_BLOCK_16) { /* size of command constraints (basically free-form directives) */ if (strsame (Command, CONTROL_NOTE, strlen(CONTROL_NOTE)) && strlen(Command) > 15) dlm820 = true; else if (plen) dlm820 = true; else dlm820 = false; if (dlm820) return ("!requires VMS V8.2 DLM (clusterwide)"); } if (strsame (Command, CONTROL_LIST, -1)) { /**********************************/ /* CLI special case, /DO=LIST/ALL */ /**********************************/ ServerCount = InstanceLockList (LockIndex, ", ", &cptr); FaoToBuffer (String, sizeof(String), 0, "!UL server!%s; !AZ", ServerCount, ServerCount ? cptr : "?"); if (cptr) VmFree (cptr, FI_LI); return (String); } ServerCount = InstanceLockList (LockIndex, ", ", &cptr); InstanceLockNotifyNow (LockIndex, Command); FaoToBuffer (String, sizeof(String), 0, "!UL instance!%s notified; !AZ", ServerCount, ServerCount ? cptr : "?"); if (cptr) VmFree (cptr, FI_LI); return (String); } if (strsame (Command, CONTROL_LOG_FORMAT_AS, strlen(CONTROL_LOG_FORMAT_AS)) || strsame (Command, CONTROL_LOG_OPEN_AS, strlen(CONTROL_LOG_OPEN_AS)) || strsame (Command, CONTROL_LOG_PERIOD_AS, strlen(CONTROL_LOG_PERIOD_AS)) || strsame (Command, CONTROL_LOG_REOPEN_AS, strlen(CONTROL_LOG_REOPEN_AS))) return ("!this directive is OBSOLETE"); if (ControlDoAllHttpd) { if (strsame (Command, CONTROL_SSL_PKPASSWD, -1)) return ("!cannot do this via /ALL"); } return ("!directive not understood"); } /*****************************************************************************/ /* The EX (exclusive) lock enqueued by the controlling process has been dequeued allowing this AST to be delivered. The control directive is now in the lock value block. Do what it is requesting. This AST is required to allow AdminControl() to use it as part of an executing server. The CONTROL_BUFFER value indicates the server should examine 'ControlBuffer' in the global section shared memory for a this-system-only command. */ ControlHttpdAst (struct lksb *lksbptr) { BOOL WithExtremePrejudice; int clen, status, ConnectNumber = 0, StartupMax; char *cptr, *sptr, *zptr; char UserName [64] = "", RemoteUser [64] = "", RequestUri [64] = "", ScriptName [64] = "", ScriptFileName [64] = ""; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD__OTHER)) WatchThis (NULL, FI_LI, WATCH_MOD__OTHER, "ControlHttpdAst() !&F !UL !&Z", &ControlHttpdAst, lksbptr, (char*)lksbptr->lksb$b_valblk); /* get the lock value block written by the dequeued EX mode lock */ cptr = ControlCommandPtr = (char*)lksbptr->lksb$b_valblk; clen = strlen(cptr); ControlMessage (cptr); if (WATCH_CAT && Watch.Category) WatchThis (NULL, FI_LI, WATCH_NOTICED, "HTTPD/DO=\'!AZ\'", cptr); #ifndef __VAX if (strsame (cptr, CONTROL_ALIGN_FAULT, sizeof(CONTROL_ALIGN_FAULT)-1)) { while (*cptr && *cptr != '=') cptr++; if (*cptr == '=') cptr++; HttpdAlignFault (cptr); } else #endif if (strsame (cptr, CONTROL_AUTH_LOAD1, clen) || strsame (cptr, CONTROL_AUTH_LOAD2, clen)) AuthConfigInit (); else if (strsame (cptr, CONTROL_AUTH_PURGE, clen)) AuthCachePurge (true); else if (strsame (cptr, CONTROL_AUTH_SKELKEY, sizeof(CONTROL_AUTH_SKELKEY)-1)) { /* turn minutes set by ControlCommand() into seconds into the future */ InstanceMutexLock (INSTANCE_MUTEX_HTTPD); if (HttpdGblSecPtr->AuthSkelKeyHttpdTickSecond > 0 && HttpdGblSecPtr->AuthSkelKeyHttpdTickSecond <= 10080) HttpdGblSecPtr->AuthSkelKeyHttpdTickSecond = HttpdTickSecond + HttpdGblSecPtr->AuthSkelKeyHttpdTickSecond * 60; InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD); } else if (strsame (cptr, CONTROL_CACHE_ON, clen)) CacheEnabled = true; else if (strsame (cptr, CONTROL_CACHE_OFF, clen)) CacheEnabled = false; else if (strsame (cptr, CONTROL_CACHE_PURGE, clen)) CachePurge (false, NULL, NULL); else if ((WithExtremePrejudice = strsame (cptr, CONTROL_DCL_DELETE, sizeof(CONTROL_DCL_DELETE)-1)) || strsame (cptr, CONTROL_DCL_PURGE, sizeof(CONTROL_DCL_PURGE)-1)) { while (*cptr && *cptr != '=') cptr++; if (*cptr == '=') cptr++; if (isdigit(*cptr)) ConnectNumber = atoi(cptr); while (*cptr && *cptr != '=') cptr++; if (*cptr == '=') { cptr++; if (strsame (cptr, "FILE=", 5)) strzcpy (ScriptFileName, cptr+5, sizeof(ScriptFileName)); else if (strsame (cptr, "SCRIPT=", 7)) strzcpy (ScriptName, cptr+7, sizeof(ScriptName)); else if (strsame (cptr, "USER=", 5)) strzcpy (UserName, cptr+5, sizeof(UserName)); else { ErrorNoticed (NULL, 0, "unknown control directive", FI_LI); return; } } DclControlPurgeScriptProcesses (WithExtremePrejudice, UserName, ScriptName, ScriptFileName); } else if (strsame (cptr, CONTROL_DECNET_DISCONNECT, clen)) DECnetControlDisconnect (false); else if (strsame (cptr, CONTROL_DECNET_PURGE, clen)) DECnetControlDisconnect (true); else if (strsame (cptr, CONTROL_EXIT, clen)) { /* stop the server from receiving incoming requests */ NetShutdownServerSocket (); if (NetConnectProcessing) { /* this will now be handled by RequestEnd() */ ControlExitRequested = true; } else ControlDelay (CONTROL_DELAY_EXIT); } else if (strsame (cptr, CONTROL_EXIT_NOW, clen)) ControlDelay (CONTROL_DELAY_EXIT); else if (strsame (cptr, CONTROL_INSTANCE_ACTIVE, clen)) NetActive (false); else if (strsame (cptr, CONTROL_INSTANCE_PASSIVE, clen)) NetPassive (); else if (strsame (cptr, CONTROL_INSTANCE, sizeof(CONTROL_INSTANCE)-1)) { while (*cptr && *cptr != '=') cptr++; if (*cptr == '=') cptr++; if (strsame (cptr, "max", -1)) StartupMax = 0; else if (strsame (cptr, "cpu", -1)) StartupMax = INSTANCE_PER_CPU; else StartupMax = atoi(cptr); InstanceMutexLock (INSTANCE_MUTEX_HTTPD); HttpdGblSecPtr->InstanceStartupMax = StartupMax; InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD); } else if (strsame (cptr, CONTROL_LOG_OPEN, clen)) Logging (NULL, LOGGING_OPEN); else if (strsame (cptr, CONTROL_LOG_REOPEN, clen)) { /* close then open */ if (VMSok (Logging (NULL, LOGGING_CLOSE))) Logging (NULL, LOGGING_OPEN); } else if (strsame (cptr, CONTROL_LOG_CLOSE, clen)) Logging (NULL, LOGGING_CLOSE); else if (strsame (cptr, CONTROL_LOG_FLUSH, clen)) Logging (NULL, LOGGING_FLUSH); else if (strsame (cptr, CONTROL_MAP_LOAD1, clen) || strsame (cptr, CONTROL_MAP_LOAD2, clen)) MapUrl_ControlReload (); else if (strsame (cptr, CONTROL_NET_PURGE_ALL, sizeof(CONTROL_NET_PURGE_NOW)-1) || strsame (cptr, CONTROL_NET_PURGE_NOW, sizeof(CONTROL_NET_PURGE_ALL)-1)) NetControl (-1, NULL); else if (strsame (cptr, CONTROL_NET_PURGE_URI, sizeof(CONTROL_NET_PURGE_URI)-1)) { cptr += sizeof(CONTROL_NET_PURGE_URI)-1; if (*cptr == '=') cptr++; strzcpy (RequestUri, cptr, sizeof(RequestUri)); NetControl (0, RequestUri); } else if (strsame (cptr, CONTROL_NET_PURGE, sizeof(CONTROL_NET_PURGE)-1)) { cptr += sizeof(CONTROL_NET_PURGE)-1; if (*cptr == '=') cptr++; ConnectNumber = atoi(cptr); NetControl (ConnectNumber, NULL); } else if (strsame (cptr, CONTROL_NET_RESUME, clen) || strsame (cptr, CONTROL_NET_NOSUSPEND, clen)) NetResume (); else if (strsame (cptr, CONTROL_NET_SUSPEND, clen)) NetSuspend (false); else if (strsame (cptr, CONTROL_NET_SUSPEND_NOW, clen)) NetSuspend (true); else if (strsame (cptr, CONTROL_NOTE, sizeof(CONTROL_NOTE)-1)) MetaConNoteThis (cptr+sizeof(CONTROL_NOTE)-1); else if (strsame (cptr, CONTROL_PROXY_ON, clen)) { InstanceMutexLock (INSTANCE_MUTEX_HTTPD); ProxyServingEnabled = ProxyAccountingPtr->ServingEnabled = true; InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD); } else if (strsame (cptr, CONTROL_PROXY_OFF, clen)) { InstanceMutexLock (INSTANCE_MUTEX_HTTPD); ProxyServingEnabled = ProxyAccountingPtr->ServingEnabled = false; InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD); } else if (strsame (cptr, CONTROL_PROXY_PURGE_BCKGRND, clen)) ProxyMaintScanBegin (PROXY_MAINT_SCAN_BCKGRND); else if (strsame (cptr, CONTROL_PROXY_PURGE_REACTIVE, clen)) ProxyMaintScanBegin (PROXY_MAINT_SCAN_REACTIVE); else if (strsame (cptr, CONTROL_PROXY_PURGE_ROUTINE, clen)) ProxyMaintScanBegin (PROXY_MAINT_SCAN_ROUTINE); else if (strsame (cptr, CONTROL_PROXY_PURGE_HOST, clen)) TcpIpHostCacheSupervisor ((unsigned int)-1); else if (strsame (cptr, CONTROL_PROXY_STATISTICS, clen)) ProxyMaintScanBegin (PROXY_MAINT_SCAN_STATISTICS); else if (strsame (cptr, CONTROL_PROXY_STOP_SCAN, clen)) ProxyMaintScanBegin (PROXY_MAINT_SCAN_STOP); else if (strsame (cptr, CONTROL_RESTART, clen)) ControlDelay (CONTROL_DELAY_RESTART); else if (strsame (cptr, CONTROL_RESTART_NOW, clen)) ControlDelay (CONTROL_DELAY_RESTART_NOW); else if (strsame (cptr, CONTROL_RESTART_QUIET, clen)) ControlDelay (CONTROL_DELAY_RESTART_QUIET); else if (strsame (cptr, CONTROL_SSL_CA_LOAD, clen)) SesolaControlReloadCA (); else if (WithExtremePrejudice = strsame (cptr, CONTROL_THROTTLE_TERMINATE, sizeof(CONTROL_THROTTLE_TERMINATE)-1) || strsame (cptr, CONTROL_THROTTLE_RELEASE, sizeof(CONTROL_THROTTLE_RELEASE)-1)) { while (*cptr && *cptr != '=') cptr++; if (*cptr == '=') cptr++; if (isdigit(*cptr)) ConnectNumber = atoi(cptr); while (*cptr && *cptr != '=') cptr++; if (*cptr == '=') { cptr++; if (strsame (cptr, "REMOTE=", 7)) strzcpy (RemoteUser, cptr+7, sizeof(RemoteUser)); else if (strsame (cptr, "SCRIPT=", 7)) strzcpy (ScriptName, cptr+7, sizeof(ScriptName)); else if (strsame (cptr, "USER=", 5)) /* backward compatible with REMOTE= */ strzcpy (RemoteUser, cptr+5, sizeof(RemoteUser)); else { ErrorNoticed (NULL, 0, "unknown control directive", FI_LI); return; } } ThrottleControl (WithExtremePrejudice, ConnectNumber, RemoteUser, ScriptName); } else if (strsame (cptr, CONTROL_WEBSOCKET_DISCONNECT, sizeof(CONTROL_WEBSOCKET_DISCONNECT)-1)) { while (*cptr && *cptr != '=') cptr++; if (*cptr == '=') cptr++; if (isdigit(*cptr)) ConnectNumber = atoi(cptr); while (*cptr && *cptr != '=') cptr++; if (*cptr == '=') { cptr++; if (strsame (cptr, "SCRIPT=", 7)) strzcpy (ScriptName, cptr+7, sizeof(ScriptName)); else if (strsame (cptr, "USER=", 5)) strzcpy (UserName, cptr+5, sizeof(UserName)); else { ErrorNoticed (NULL, 0, "unknown control directive", FI_LI); return; } } WebSockControl (ConnectNumber, ScriptName, UserName); } else if (strsame (cptr, CONTROL_THROTTLE_ZERO, clen)) ThrottleZero (); else if (strsame (cptr, CONTROL_ZERO, clen)) ControlZeroAccounting (); else if (strsame (cptr, CONTROL_ZERO_NOTICED, clen)) ControlZeroNoticed (); else if (strsame (cptr, CONTROL_ZERO_PROXY, clen)) ControlZeroProxyAccounting (); else ErrorNoticed (NULL, 0, "unknown control directive", FI_LI); } /*****************************************************************************/ /* Used to "sleep" with slightly greater granularity than sleep() provides. This is necessary due to the polling of events required under some circumstances. Cannot be used if user-mode ASTs are unavailable (e.g. when processing commands delivered using blocking lock ASTs). */ ControlSleep (int MilliSeconds) { static unsigned long OneMilliSecondDelta [2] = { -10000, -1 }; int status; unsigned long ScratchBinTime [2]; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD__OTHER)) WatchThis (NULL, FI_LI, WATCH_MOD__OTHER, "ControlSleep() !UL", MilliSeconds); if (MilliSeconds) { memcpy (&ScratchBinTime, &OneMilliSecondDelta, sizeof(ScratchBinTime)); status = lib$mult_delta_time (&MilliSeconds, &ScratchBinTime); if (VMSnok (status)) exit (status); status = sys$setimr (0, &ScratchBinTime, &ControlSleep, 0, 0); if (VMSnok (status)) exit (status); sys$hiber (); return; } sys$wake (0, 0); } /*****************************************************************************/ /* Using the supplied PID fill out 'ControlPid', 'ControlProcessName, abnd 'ControlUserName' with the appropriate information. If 'Pid' is zero then it will be the datils of the current process. */ ControlAccount (unsigned long Pid) { static BOOL NoWorldPriv; static unsigned long GetJpiControlFlags = JPI$M_IGNORE_TARGET_STATUS; 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(ControlPid), JPI$_PID, &ControlPid, 0 }, { sizeof(ControlNodeName), JPI$_NODENAME, &ControlNodeName, 0 }, { sizeof(ControlProcessName), JPI$_PRCNAM, &ControlProcessName, 0 }, { sizeof(ControlUserName), JPI$_USERNAME, &ControlUserName, 0 }, { 0,0,0,0 } }; int status; char *cptr; IO_SB IOsb; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD__OTHER)) WatchThis (NULL, FI_LI, WATCH_MOD__OTHER, "ControlAccount() !8XL", Pid); /* use WORLD to allow access to other processes */ status = sys$setprv (1, &WorldMask, 0, 0); if (VMSnok (status) || status == SS$_NOTALLPRIV) { if (!NoWorldPriv) { NoWorldPriv = true; fprintf (stdout, "%%HTTPD-W-CONTROL, installed without WORLD privilege\n"); } } if (!NoWorldPriv && Pid) { status = sys$getjpiw (EfnWait, &Pid, 0, &JpiItems, &IOsb, 0, 0); if (VMSok (status)) status = IOsb.Status; } else status = SS$_NOPRIV; if (VMSok (status)) { ControlNodeName[15] = '\0'; for (cptr = ControlNodeName; *cptr && *cptr != ' '; cptr++); *cptr = '\0'; (cptr = ControlProcessName)[15] = '\0'; cptr--; while (cptr > ControlProcessName && *cptr == ' ') cptr--; *cptr = '\0'; ControlUserName[12] = '\0'; for (cptr = ControlUserName; *cptr && *cptr != ' '; cptr++); *cptr = '\0'; } else { strcpy (ControlNodeName, "?"); strcpy (ControlProcessName, "?"); strcpy (ControlUserName, "?"); } sys$setprv (0, &WorldMask, 0, 0); } /*****************************************************************************/ /* Report a control request/response, to the process log (SYS$OUTPUT) and optionally as OPCOM messages. */ ControlMessage (char *Message) { static unsigned long LkiValBlkLen, Lki_XVALNOTVALID; static char LkiValBlk [LOCK_VALUE_BLOCK_64]; static VMS_ITEM_LIST3 LkiItems [] = { /* careful, values are dynamically assigned in code below! */ { 0, 0, 0, 0 }, /* reserved for LKI$_[X]VALBLK item */ { 0, 0, 0, 0 }, /* reserved for LKI$_XVALNOTVALID item */ {0,0,0,0} }; int status; IO_SB IOsb; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD__OTHER)) WatchThis (NULL, FI_LI, WATCH_MOD__OTHER, "ControlMessage() !&Z", Message); if (SysInfo.LockValueBlockSize == LOCK_VALUE_BLOCK_64) { LkiItems[0].buf_len = LOCK_VALUE_BLOCK_64; LkiItems[0].buf_addr = &LkiValBlk; LkiItems[0].item = LKI$_XVALBLK; LkiItems[0].ret_len = &LkiValBlkLen; LkiItems[1].buf_len = sizeof(Lki_XVALNOTVALID); LkiItems[1].buf_addr = &Lki_XVALNOTVALID; LkiItems[1].item = LKI$_XVALNOTVALID; } else { LkiItems[0].buf_len = LOCK_VALUE_BLOCK_16; LkiItems[0].buf_addr = &LkiValBlk; LkiItems[0].item = LKI$_VALBLK; LkiItems[0].ret_len = &LkiValBlkLen; /* in this case this terminates the item list */ LkiItems[1].buf_len = 0; LkiItems[1].buf_addr = 0; LkiItems[1].item = 0; Lki_XVALNOTVALID = 0; } memset (LkiValBlk, 0, sizeof(LkiValBlk)); sys$setprv (1, &SysLckMask, 0, 0); status = sys$getlkiw (EfnWait, &InstanceLockTable[INSTANCE_CLUSTER_CONTROL].Lksb.lksb$l_lkid, &LkiItems, &IOsb, 0, 0, 0); sys$setprv (0, &SysLckMask, 0, 0); if (VMSok (status)) status = IOsb.Status; if (VMSnok (status)) ErrorNoticed (NULL, status, NULL, FI_LI); if (Lki_XVALNOTVALID) { /* hmmm, change in cluster composition? whatever! go back to 16 bytes */ SysInfo.LockValueBlockSize = LOCK_VALUE_BLOCK_16; ErrorNoticed (NULL, SS$_XVALNOTVALID, ErrorXvalNotValid, FI_LI); } LkiValBlk[LkiValBlkLen] = '\0'; if (sscanf (LkiValBlk, "%x", &ControlPid) < 1) ControlPid = 0; ControlAccount (ControlPid); FaoToStdout ("%HTTPD-!&?E\rI\r-CONTROL, \ !20%D, !8XL !AZ !AZ \"!AZ\", \'!AZ\'\n", Message[0] == '!', 0, ControlPid, ControlNodeName, ControlUserName, ControlProcessName, Message); if (OpcomMessages & OPCOM_CONTROL) FaoToOpcom ("%HTTPD-!&?E\rI\r-CONTROL, \ !8XL !AZ !AZ \"!AZ\", \'!AZ\'", Message[0] == '!', ControlPid, ControlNodeName, ControlUserName, ControlProcessName, Message); if (WATCH_CAT && Watch.Category) { WatchThis (NULL, FI_LI, WATCH_NOTICED, "HTTPD/DO="); WatchDataFormatted ( "%HTTPD-!&?E\rI\r-CONTROL, !20%D, !8XL !AZ !AZ \"!AZ\", \'!AZ\'\n", Message[0] == '!', 0, ControlPid, ControlNodeName, ControlUserName, ControlProcessName, Message); } } /*****************************************************************************/ /* Exits or restarts the server after a short delay. This delay is introduced to give a controlling request (e.g. via the Admin Menu) a chance to complete processing (e.g. deliver success message) before the action is taken. There is an additional random delay introduced with multiple instances to prevent mass suicide when RESTART=NOW and reducing the number of instances. Is called directly by the requesting function with a parameter of CONTROL_DELAY_EXIT or CONTROL_DELAY_RESTART. A timer with AST delivery back to this function is set, the AST parameter the same as the original with a flag inserted indicating it really should be done *this* time! */ ControlDelay (int Action) { int status; unsigned long DelayDelta [2]; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD__OTHER)) WatchThis (NULL, FI_LI, WATCH_MOD__OTHER, "ControlDelay() !UL", Action); if (!(Action & CONTROL_DELAY_DO)) { DelayDelta [0] = -2500000; /* 250mS */ /* if multi instances introduce a further, random delay of 250-750mS */ if (InstanceNodeCurrent > 1 && !InstanceNodeSupervisor) DelayDelta[0] += -2500000 + (-50000 * HttpdNumTime[6]); DelayDelta [1] = -1; status = sys$setimr (0, &DelayDelta, &ControlDelay, Action | CONTROL_DELAY_DO, 0); if (VMSnok (status)) ErrorExitVmsStatus (status, NULL, FI_LI); return; } switch (Action & ~CONTROL_DELAY_DO) { case CONTROL_DELAY_EXIT : ExitStatus = SS$_NORMAL; HttpdExit (&ExitStatus); /* record server event */ GraphActivityEvent (ACTIVITY_DELPRC); sys$delprc (0, 0); case CONTROL_DELAY_RESTART : ControlRestartRequested = true; /* start the server supervisor in case it's not running */ if (!HttpdTicking) HttpdTick (0); /* this will now be handled by InstanceSupervisor() */ return; case CONTROL_DELAY_RESTART_QUIET : ControlRestartQuiet = true; /* start the server supervisor in case it's not running */ if (!HttpdTicking) HttpdTick (0); /* this will now be handled by InstanceSupervisor() */ return; case CONTROL_DELAY_RESTART_NOW : exit (SS$_NORMAL); default : ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); } } /*****************************************************************************/ /* Zero server accounting structure and service counters. These are both process-local and node-global. Each gets reset under differing circumstances. */ ControlZeroAccounting () { int idx, StartupCount, ZeroedCount; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD__OTHER)) WatchThis (NULL, FI_LI, WATCH_MOD__OTHER, "ControlZeroAccounting() !UL !&B", InstanceNodeConfig, InstanceNodeSupervisor); /* these two get done on a per-process basis regardless */ ThrottleZero (); NetServiceZeroAccounting (); /* in a multi-instance config, shared data only by the supervisor */ if (InstanceNodeConfig > 1 && !InstanceNodeSupervisor) return; InstanceMutexLock (INSTANCE_MUTEX_HTTPD); StartupCount = AccountingPtr->StartupCount; ZeroedCount = AccountingPtr->ZeroedCount; memset (AccountingPtr, 0, sizeof(ACCOUNTING_STRUCT)); AccountingPtr->StartupCount = StartupCount; AccountingPtr->ZeroedCount = ZeroedCount + 1; memset (ProxyAccountingPtr, 0, sizeof(PROXY_ACCOUNTING_STRUCT)); ProxyAccountingPtr->ServingEnabled = ProxyServingEnabled; ProxyAccountingPtr->FreeSpaceAvailable = ProxyCacheFreeSpaceAvailable; for (idx = 1; idx <= INSTANCE_MUTEX_COUNT; idx++) HttpdGblSecPtr->MutexCount[idx] = HttpdGblSecPtr->MutexWaitCount[idx] = 0; InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD); } /*****************************************************************************/ /* Zero proxy accounting structure. */ ControlZeroProxyAccounting () { int idx, StartupCount, ZeroedCount; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD__OTHER)) WatchThis (NULL, FI_LI, WATCH_MOD__OTHER, "ControlZeroProxyAccounting() !UL !&B", InstanceNodeConfig, InstanceNodeSupervisor); /* in a multi-instance config, shared data only by the supervisor */ if (InstanceNodeConfig > 1 && !InstanceNodeSupervisor) return; InstanceMutexLock (INSTANCE_MUTEX_HTTPD); memset (ProxyAccountingPtr, 0, sizeof(PROXY_ACCOUNTING_STRUCT)); ProxyAccountingPtr->ServingEnabled = ProxyServingEnabled; ProxyAccountingPtr->FreeSpaceAvailable = ProxyCacheFreeSpaceAvailable; InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD); } /*****************************************************************************/ /* Zero the errors noticed accounting data. */ ControlZeroNoticed () { /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD__OTHER)) WatchThis (NULL, FI_LI, WATCH_MOD__OTHER, "ControlZeroNoticed()"); InstanceMutexLock (INSTANCE_MUTEX_HTTPD); AccountingPtr->ErrorsNoticedCount = 0; PUT_ZERO_QUAD (AccountingPtr->ErrorsNoticedBinTime); InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD); } /*****************************************************************************/