/*****************************************************************************/ /* spawn.c Spawn a (sub)process and retrieve its output to an internal buffer. VERSION HISTORY --------------- 21-FEB-2019 MGD initial */ /*****************************************************************************/ #include "wasdoc.h" #include #include #include #include #include #define SPAWN_MAXMSG 2048 #define SPAWN_BUFQUO 65000 /* VAX limit? */ #define SPAWN_MBX_LNM "WASDOC_SPAWN_OUTPUT" extern int dbug, isCgi, isCgiPlus; /*****************************************************************************/ /* Spawn a subprocess to execute the supplied command. Use NOWAIT flag and explicitly hibernate at USER mode otherwise the USER mode output ASTs won't be delivered. "Translate" VMS status into errno code. Insert the output into the document as required. Bit convoluted but return negative for test true, 0 for test false and output OK, or positive errno number. */ int SpawnCommand ( struct wasdoc_st *docptr, char *cmd, char *test ) { static long flags = 0x01; static $DESCRIPTOR (CmdDsc, ""); static $DESCRIPTOR (MsgDsc, ""); static $DESCRIPTOR (MbxLnmDsc, SPAWN_MBX_LNM); static $DESCRIPTOR (NlDsc, "NL:"); int ignore = 0, noescape = 0, reuse = 0, trim = 0; uint status; ushort slen; char *cptr, *sptr; if (dbug>0) dbugThis (FI_LI, "SpawnCommand() |%s|", cmd); cptr = cmd; while (*cptr == '!' || *cptr == '\"' || *cptr == '-' || *cptr == '&') { if (*cptr == '!') ignore = *cptr++; if (*cptr == '\"') noescape = *cptr++; if (*cptr == '-') trim = *cptr++; if (*cptr == '&') reuse = *cptr++; } if (*cptr == '\\') cptr++; if (spawnAuth (docptr) < 0) { /* not authorised */ if (!test) wasDocInsertStatus (docptr, SS$_AUTHFAIL, 0); if (ignore) return (0); return (ECHILD); } if (reuse) { /* reuse previous output */ if (!docptr->spawnOutLength) return (EINVAL); if (test) if (testCondition (docptr, docptr->spawnOutPtr, test)) return (-1); else return (0); else renderInsertText (docptr, docptr->spawnOutPtr, noescape); return (0); } while (isspace(*cptr)) cptr++; if (!*cptr) if (ignore) return (0); else return (EINVAL); for (sptr = cptr; *sptr; sptr++); CmdDsc.dsc$a_pointer = cptr; CmdDsc.dsc$w_length = sptr - cptr; /* a temporary mailbox */ status = sys$crembx (0, &docptr->spawnMbx, SPAWN_MAXMSG, SPAWN_BUFQUO, 0, 0, &MbxLnmDsc, 0, 0); if (dbug>0) dbugThis (FI_LI, "sys$crembx() %%X%08.08X", status); if (!(status & 1)) { if (!test) wasDocInsertStatus (docptr, SS$_AUTHFAIL, 0); if (ignore) return (0); return (EIO); } /* initiate reads from soon-to-be spawned process */ if (docptr->spawnOutPtr) { free (docptr->spawnOutPtr); docptr->spawnOutPtr = NULL; docptr->spawnOutLength = docptr->spawnOutSize = 0; } SpawnOutAst (docptr); if (docptr->insight >= 4) wasDocInsight (docptr, "%s", cptr); status = lib$spawn (&CmdDsc, &NlDsc, &MbxLnmDsc, &flags, 0, &docptr->spawnPID, &docptr->spawnStatus, 0, SpawnWake, 0, 0, 0, 0); if (dbug>0) dbugThis (FI_LI, "lib$spawn () %08.08X %%X%08.08X\n", docptr->spawnPID, status); /* hibernate at this (USER) mode */ sys$hiber (); if (dbug>0) dbugThis (FI_LI, "lib$spawn() %08.08X", docptr->spawnStatus); sys$dassgn (docptr->spawnMbx); docptr->spawnMbx = 0; if ((status & 1) && !(docptr->spawnStatus & 1)) status = docptr->spawnStatus; if ((status & 1) && docptr->spawnOutStatus != SS$_ENDOFFILE) status = docptr->spawnOutStatus; if (docptr->spawnOutLength) if (docptr->insight >= 5) wasDocInsight (docptr, "
%s
", docptr->spawnOutPtr); if (trim && docptr->spawnOutLength) { /* eliminate multiple blank lines and leading/trailing white-space */ sptr = docptr->spawnOutPtr + docptr->spawnOutLength; while (sptr > docptr->spawnOutPtr && isspace(*(sptr-1))) sptr--; *sptr = '\0'; for (sptr = cptr = docptr->spawnOutPtr; *cptr; cptr++) if (!isspace(*cptr)) break; for (; *cptr; *sptr++ = *cptr++) { if (*cptr != '\n') continue; if (*(USHORTPTR)(sptr-2) != '\n\n') continue; while (*cptr == '\n') cptr++; } *sptr = '\0'; docptr->spawnOutLength = sptr - docptr->spawnOutPtr; } if (!(status & 1)) { if (docptr->insight >= 4) wasDocInsight (docptr, "%%X%08.08X", status); if (!test) wasDocInsertStatus (docptr, status, 0); return (ECHILD); } if (test) if (testCondition (docptr, docptr->spawnOutPtr, test)) return (-1); else return (0); else renderInsertText (docptr, docptr->spawnOutPtr, noescape); return (0); } /*****************************************************************************/ /* Subprocess has completed. Bring the parent (this process) out of hibernation. */ void SpawnWake () { sys$wake (0, 0); } /*****************************************************************************/ /* Buffer output from subprocess. */ void SpawnOutAst (struct wasdoc_st *docptr) { int status; char *cptr; if (dbug>0) dbugThis (FI_LI, "SpawnOutAst() %%X%08.08X", docptr->spawnIOsb.iosb$w_status); if (docptr->spawnOutPtr) { docptr->spawnOutStatus = docptr->spawnIOsb.iosb$w_status; if (!(docptr->spawnOutStatus & 1)) { /* mailbox read failed */ sys$dassgn (docptr->spawnMbx); docptr->spawnMbx = 0; return; } cptr = docptr->spawnOutPtr + docptr->spawnOutLength; cptr[docptr->spawnIOsb.iosb$w_bcnt] = '\n'; cptr[docptr->spawnIOsb.iosb$w_bcnt+1] = '\0'; docptr->spawnOutLength += docptr->spawnIOsb.iosb$w_bcnt + 1; if (dbug>0) dbugThis (FI_LI, "%d |%s|\n", docptr->spawnIOsb.iosb$w_bcnt, cptr); } if (docptr->spawnOutSize - docptr->spawnOutLength <= SPAWN_MAXMSG) { if (docptr->spawnOutSize) docptr->spawnOutSize += SPAWN_MAXMSG; else docptr->spawnOutSize = SPAWN_MAXMSG * 2; docptr->spawnOutPtr = realloc (docptr->spawnOutPtr, docptr->spawnOutSize + 32); if (!docptr->spawnOutPtr) exit (vaxc$errno); } cptr = docptr->spawnOutPtr + docptr->spawnOutLength; status = sys$qio (0, docptr->spawnMbx, IO$_READLBLK, &docptr->spawnIOsb, SpawnOutAst, docptr, cptr, SPAWN_MAXMSG, 0, 0, 0, 0); if (!(status & 1)) { /* $QIO failed */ sys$dassgn (docptr->spawnMbx); docptr->spawnMbx = 0; } } /*****************************************************************************/ /* For a dynamic document authorise the spawn. */ int spawnAuth (struct wasdoc_st *docptr) { static unsigned short slen; static unsigned long lnmAttributes, lnmIndex; static char lnmValue [256]; static $DESCRIPTOR (lnmNameDsc, SPAWN_LOGNAM); static $DESCRIPTOR (lnmSystemDsc, "LNM$SYSTEM"); static struct { short int buf_len; short int item; void *buf_addr; unsigned short *ret_len; } lnmItems [] = { { sizeof(lnmIndex), LNM$_INDEX, &lnmIndex, 0 }, { sizeof(lnmAttributes), LNM$_ATTRIBUTES, &lnmAttributes, 0 }, { sizeof(lnmValue)-1, LNM$_STRING, lnmValue, &slen }, { 0,0,0,0 } }; int status; /*********/ /* begin */ /*********/ if (docptr->spawnAuth) return (docptr->spawnAuth); if (docptr->isStatic) return (docptr->spawnAuth = 1); for (lnmIndex = 0; lnmIndex <= 127; lnmIndex++) { status = sys$trnlnm (0, &lnmSystemDsc, &lnmNameDsc, 0, &lnmItems); if (dbug>0) dbugThis (FI_LI, "%%X%08.08X %d", status, (lnmAttributes & LNM$M_EXISTS)); if ((status & 1) && (lnmAttributes & LNM$M_EXISTS)) { lnmValue[slen] = '\0'; if (dbug>0) dbugThis (FI_LI, "|%s|%s|", docptr->dname, lnmValue); if (!strcasecmp (docptr->dname, lnmValue)) { docptr->spawnAuth = 1; break; } } else { docptr->spawnAuth = -1; break; } } return (docptr->spawnAuth); } /*****************************************************************************/