/*****************************************************************************/ /* 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. 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 vaxc$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>1) 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); 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 if (noescape) wasDocAsIs (docptr, docptr->spawnOutPtr); else wasDocEntify (docptr, docptr->spawnOutPtr); 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>1) dbugThis (FI_LI, "sys$crembx() %%X%08.08X", status); if (!(status & 1)) { if (!test) wasDocInsertStatus (docptr, SS$_AUTHFAIL); 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>1) dbugThis (FI_LI, "lib$spawn () %08.08X %%X%08.08X\n", docptr->spawnPID, status); /* hibernate at this (USER) mode */ sys$hiber (); if (dbug>1) 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); return (ECHILD); } if (test) if (testCondition (docptr, docptr->spawnOutPtr, test)) return (-1); else return (0); else if (noescape) wasDocAsIs (docptr, docptr->spawnOutPtr); else wasDocEntify (docptr, docptr->spawnOutPtr); 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>1) 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>1) 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>1) dbugThis (FI_LI, "%%X%08.08X %d", status, (lnmAttributes & LNM$M_EXISTS)); if ((status & 1) && (lnmAttributes & LNM$M_EXISTS)) { lnmValue[slen] = '\0'; if (dbug>1) 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); } /*****************************************************************************/