#!mkcmd # $Id: efmd.m,v 1.21 2012/09/03 19:10:59 ksb Exp $ # Process a list file (string) against selected hosts to stdout # # $Compile: ${mkcmd:-mkcmd} -I../msrc -I../hxmd -G -n efmd %f comment "%c %kCompile: $%{cc-cc%} -g -Wall -o %%[f.-$] %%f %k%k" from '' from '' from '' from '' from '' from '' from '' from '' from '' from '' from '' from '' from '' from '' from '' from '' basename "efmd" "" from '"machine.h"' require "util_errno.m" "util_ppm.m" "util_pipefit.m" require "std_help.m" "std_version.m" augment action 'V' { user "Version();" } augment boolean 'v' { hidden } %i static const char rcsid[] = "$Id: efmd.m,v 1.21 2012/09/03 19:10:59 ksb Exp $"; #if !defined(DEF_HXMD_LIB) #define DEF_HXMD_LIB ".:/usr/local/lib/hxmd:/usr/local/lib/distrib" #endif static const char *apcM4Path[] = { "/usr/local/bin/m4", #if defined(M4_SYS_PATH) M4_SYS_PATH, #endif "/usr/bin/m4", "/usr/ccs/bin/m4", // Fall back to a crufty old m4 on Solaris --jad (char *)0 }; static const char *pcM4Found = (const char *)0; /* hxmd forces slot-0 to be +x, we don't */ #undef SLOT_0_ORBITS #if !defined(HX_BYTES_PER_FILE) #define HX_BYTES_PER_FILE (MAXPATHLEN+4) #endif static const char acHxmdLib[] = "HXMD_LIB"; static char acHxmdSearch[] = DEF_HXMD_LIB, acMySpace[HX_BYTES_PER_FILE]; %% function 'DIU' { named "AddSwitch" param "m4-opts" help "pass an option down to m4" } type "argv" 'j' { named "pPPMM4Prep" param "m4prep" help "include specified files in every m4 process\'s arguments" } char* 'd' { named "pcDebug" param "flags" help "debug flags we pass on to m4 ('?' for help)" } # take arguments mostly like hxmd does integer 'F' { named 'iCmdLits' init "1" param 'literal' help "the number of parameters which are literal text (default %qi)" } type "argv" 'T' { named "pPPMTop" param "header" help "m4 commands included at the top of the markup" } boolean 'L' { exclude 'FnT' named "fListOnly" help "output only the selected host list" list { hidden named "List" param "" help "ignored parameters" } } # We need to be compatible with any local HXMD macro munging char* 'M' { hidden named "pcXM4Define" init '"HXMD_"' help "macro filename prefix for m4 cross file references" } int named "bHasRoot" { init dynamic "0 == getuid()" } after "AfterGlow(argc, argv);" list { named "List" param "arguments" help "attribute names or files to map and extract" } init "EfmdStart();" %c static PPM_BUF PPMM4; static unsigned int uM4c = 0; /* Set up the standard zero configuration hook, start the m4 args and (ksb) * instance our temporary name-space under $TMPDIR. Then $(which m4). */ static void EfmdStart() { register char **ppcInit, *pcEnvTemp; register const char **ppcScan; auto char acTemp[MAXPATHLEN+4]; auto struct stat stCheck; if ((char *)0 == pcZeroConfig ? (0 == strcmp("-", progname) || 0 == strcmp("stdin", progname)) : (0 == strcmp("-", pcZeroConfig))) { pcZeroConfig = "-"; } else if ((char *)0 == pcZeroConfig && 0 != strcmp("efmd", progname)) { snprintf(acTemp, sizeof(acTemp)-1, "%s.cf", progname); pcZeroConfig = strdup(acTemp); } /* We might change our minds later, but assume a -o option now, * and include the printf format to install it later. /fGaveMerge/ */ util_ppm_init(& PPMM4, sizeof(char *), 256); ppcInit = util_ppm_size(& PPMM4, 256); uM4c = 0; ppcInit[uM4c++] = "m4"; ppcInit[uM4c++] = "-D"; ppcInit[uM4c++] = "%sU_MERGED=%s"; ppcInit[uM4c] = (char *)0; if ((char *)0 == (pcEnvTemp = getenv("TMPDIR")) || '\000' == *pcEnvTemp) { pcEnvTemp = "/tmp"; } snprintf(acMySpace, MAXPATHLEN, "%s/efmdXXXXXX", pcEnvTemp); /* Find the m4 binary the Customer wants to use: from the environment * or the first one in our internal list. N.B. If you set this to a * script that runs m4 on stdin then pipes the output to a shell you * may used HXMD_U_MERGED in the script, else you shan't. -- ksb */ pcM4Found = getenv("M4_PATH"); for (ppcScan = apcM4Path; (const char *)0 == pcM4Found && (const char *)0 != *ppcScan; ++ppcScan) { if (-1 == stat(*ppcScan, &stCheck) || 0 == (stCheck.st_mode & 0111)) continue; pcM4Found = *ppcScan; } if ((const char *)0 == pcM4Found) { pcM4Found = ppcInit[0]; } } /* We output almost the same version block as hxmd, because need (ksb) * to be in-sync with hxmd, and someone might have to debug that. */ static void Version() { register char *pcEnvSearch, *pcFrom, *pcSearch; auto struct stat stCheck; printf("%s: slot code: %s\n", progname, rcs_slot); printf("%s: hostdb code: %s\n", progname, rcs_hostdb); printf("%s: evector code: %s\n", progname, rcs_evector); printf("%s: default column headers: %%%s\n", progname, pcHOST); printf("%s: m4 path: %s", progname, pcM4Found); if (-1 == stat(pcM4Found, &stCheck)) { printf(" [nonexistant]"); } else if (0 == (stCheck.st_mode & 0111)) { printf(" [execute not set]"); } printf("\n%s: auto-define prefix for B,OPT_{C,X,Z},U,U_MERGED,U_SELECTED: %s\n", progname, pcXM4Define); printf("%s: template for private file space: %s\n", progname, acMySpace); if ((char *)0 != (pcEnvSearch = getenv(acHxmdLib)) && '\000' != *pcEnvSearch) { pcFrom = "environment"; pcSearch = pcEnvSearch; } else { pcFrom = "default"; pcSearch = pcHostSearch; } printf("%s: search list from $%s\n", progname, acHxmdLib); printf("%s: %s search list: %s\n", progname, pcFrom, pcSearch); printf("%s: %s double-dash directory: %s\n", progname, pcFrom, acTilde); printf("%s: cache recipe file: %s\n", progname, acDownRecipe); } #if !defined(HOST_NAME_MAX) #define HOST_NAME_MAX (MAXHOSTNAMELEN) #endif /* Pass a single letter switch or switch with a param down to m4. (ksb) * Copied from mmsrc's version, since that's closest. */ static void AddSwitch(int iOpt, char *pcArg) { register char **ppc; /* Tap -D option stream for some clues we need in the merged file */ if ('D' == iOpt && (char *)0 != pcArg) { register unsigned u; register char *pcReal; /* Eat any -D!FOO's exclaimation mark */ pcReal = pcArg; if ('_' != *pcArg && !isalnum(*pcArg)) { ++pcReal; } ppc = util_ppm_size(& PPMAutoDef, 0); for (u = 0; (char *)0 != ppc[u]; ++u) { /* search for end */ } ppc = util_ppm_size(& PPMAutoDef, ((u+1)|15)+1); ppc[u++] = pcArg; ppc[u] = (char *)0; pcArg = pcReal; } ppc = util_ppm_size(& PPMM4, (uM4c|7)+9); if ('\000' != iOpt) { ppc[uM4c] = strdup("-_"); ppc[uM4c++][1] = iOpt; } if ((char *)0 != pcArg) { ppc[uM4c++] = ('I' == iOpt && '-' == pcArg[0] && '-' == pcArg[1] && '\000' == pcArg[2]) ? acTilde : pcArg; } ppc[uM4c] = (char *)0; } /* Default missing data, debug level create temp space pattern (ksb) */ static void AfterGlow(int argc, char **argv) { if ((char *)0 != pcDebug) { AddSwitch('d', pcDebug); } else { pcDebug = ""; } fVerbose = 0; if ((char *)0 != strchr(pcDebug, '?')) { printf("%s: -d [?]*[aceflqtxV]*\n?\tdisplay these options and exit\nall others are provided by m4\n", progname); exit(0); } if ((char *)0 == mkdtemp(acMySpace)) { fprintf(stderr, "%s: mkdtemp: %s: %s\n", progname, acMySpace, strerror(errno)); exit(EX_TEMPFAIL); } } /* We just process the command-line params once for each HOST (ksb) * selected, and output the stream to m4 as a filter. What you * do with the data is up to you. This replaces "distrib -E". */ static void List(int argc, char **argv) { register int i; register char *pcFound; auto char **ppcM4, **ppcPrep, *pcHostSlot, *apcLook[2], **ppc, **ppcIsCache; auto const char *pcRecipe; auto int wExit; auto pid_t wM4, wWait; auto META_HOST *pMHAll; auto unsigned uLeftLits, uRightLits, uRightBegin; auto char acTemp[MAXPATHLEN+4]; extern char **environ; /* Build the M4 phase buffer for HXMD_PHASE=(selection|%d|cmd), */ ppc = util_ppm_size(& PPMM4, (uM4c|7)+9); ppc[uM4c++] = strdup("-D"); ppc[uM4c++] = SetupPhase("PHASE", 64); SetCurPhase("selection"); /* Put the fixed m4 prep files in the m4 template options. * Then add the dash for stdin, so m4 will read our pipe. */ if ((char **)0 != (ppc = util_ppm_size(pPPMM4Prep, 0))) { static const char acDex[] = ".m4"; register char *pcEnd; for (/*above */; (char *)0 != *ppc; ++ppc) { if ('-' != ppc[0][0] || '-' != ppc[0][1] || !('/' == ppc[0][2] || '\000' == ppc[0][2])) { AddSwitch('\000', *ppc); continue; } ppc[0] += 2; while ('/' == ppc[0][0]) { ppc[0] += 1; } if ('\000' == ppc[0][0]) { /* "/opt/hxmd/lib"+'/'+progname+".m4"+'\000' */ i = ((strlen(acTilde)+1+strlen(progname)+sizeof(acDex))|7)+1; if ((char *)0 == (pcEnd = malloc(i))) { fprintf(stderr, "%s: malloc: %s\n", progname, strerror(errno)); exit(EX_OSERR); } snprintf(pcEnd, i, "%s/%s%s", acTilde, progname, acDex); } else { /* "/opt/hxmd/lib"+'/'+ppc[0]+'\000' */ i = ((strlen(acTilde)+1+strlen(*ppc))|7)+1; if ((char *)0 == (pcEnd = malloc(i))) { fprintf(stderr, "%s: malloc: %s\n", progname, strerror(errno)); exit(EX_OSERR); } snprintf(pcEnd, i, "%s/%s", acTilde, *ppc); } AddSwitch('\000', pcEnd); } } AddSwitch('\000', "-"); uRightBegin = argc; uLeftLits = uRightLits = 0; if (0 <= iCmdLits) { uLeftLits = iCmdLits; } else { uRightLits = -iCmdLits; uRightBegin -= uRightLits; } if (!fListOnly && argc < (i = (uLeftLits+uRightLits))) { fprintf(stderr, "%s: not enough parameters for literals (%d < %u)\n", progname, argc, i); exit(EX_USAGE); } if ((char *)0 == (pcHostSlot = HostSlot(acMySpace))) { exit(EX_DATAERR); } /* The common hxmd file search mnemonic -- to me */ ppcIsCache = (char **)calloc((argc|3)+1, sizeof(char *)); wExit = 0; apcLook[1] = (char *)0; pcRecipe = (char *)0; for (i = 0; i < argc; ++i) { auto struct stat stCheck; register size_t iLen; ppcIsCache[i] = (char *)0; if (i < uLeftLits || i >= uRightBegin) { continue; } if ('-' == argv[i][0] && '\000' == argv[i][1]) { ReserveStdin(& argv[i], "files"); continue; } apcLook[0] = argv[i]; if ('.' == argv[i][0] && '/' == argv[i][1]) { /* explicity do not search for file */ } else if ((char *)0 == (pcFound = util_fsearch(apcLook, getenv(acHxmdLib)))) { fprintf(stderr, "%s: %s: cannot find file\n", progname, argv[i]); wExit = EX_NOINPUT; } else { argv[i] = pcFound; } if (0 != stat(argv[i], & stCheck)) { fprintf(stderr, "%s: stat: %s: %s\n", progname, argv[i], strerror(errno)); exit(EX_NOINPUT); } if (S_ISFIFO(stCheck.st_mode) || S_ISREG(stCheck.st_mode)) { continue; } if (!S_ISDIR(stCheck.st_mode)) { fprintf(stderr, "%s: %s: must a FIFO, regular file, or cache directory\n", progname, argv[i]); exit(EX_DATAERR); } snprintf(acTemp, sizeof(acTemp), "%s/%s", argv[i], acDownRecipe); if ((char *)0 == (argv[i] = strdup(acTemp))) { fprintf(stderr, "%s: strdup: %s\n", progname, strerror(errno)); exit(EX_TEMPFAIL); } if (0 != stat(argv[i] , &stCheck)) { fprintf(stderr, "%s: stat: %s: %s\n", progname, argv[i], strerror(errno)); exit(EX_DATAERR); } if (!S_ISFIFO(stCheck.st_mode) && !S_ISREG(stCheck.st_mode)) { fprintf(stderr, "%s: %s: must be a regular file or FIFO\n", progname, argv[i]); exit(EX_DATAERR); } /* We must get "./rXXXXXX/makeXXXXXX" at least, if $TMPDIR * is set to dot. */ pcRecipe = FindCache("make", pcHostSlot); iLen = strlen(pcRecipe); /* make room for extra \000 */ if (iLen < 22) { fprintf(stderr, "%s: hostdb.m is broken\n", progname); exit(EX_SOFTWARE); } if ((char *)0 == (ppcIsCache[i] = calloc((7|(iLen+2))+1, sizeof(char)))) { fprintf(stderr, "%s: malloc: %s\n", progname, strerror(errno)); exit(EX_OSERR); } /* stuff/make______\000 -> stuff/makeXXXXXX\000\000 * calloc put the extra \000 on the end for us, for FillSlot. */ (void)strcpy(ppcIsCache[i], pcRecipe); (void)strcpy(ppcIsCache[i]+(iLen-6), "XXXXXX"); } if (0 != wExit) { exit(wExit); } /* Read configuration data, first the -C/-X/-Z files. * Either replace the format with the payload, or shift off * the forced -D for the -o file. Then produce the host list. */ ppcM4 = util_ppm_size(& PPMM4, 0); if (fGaveMerge) { register char *pcMem; register const char *pcMerged; register size_t i; /* We know that hostdb uses the "filter" mktemp for the merge */ pcMerged = FindCache("filter", pcHostSlot); i = (15|(strlen(ppcM4[2])+strlen(pcXM4Define)+strlen(pcMerged)))+1; if ((char *)0 == (pcMem = malloc(i))) { fprintf(stderr, "%s: malloc: %ld: %s\n", progname, (long)i, strerror(errno)); exit(EX_OSERR); } snprintf(pcMem, i, ppcM4[2], pcXM4Define, pcMerged); ppcM4[2] = pcMem; } else { ppcM4[2] = ppcM4[0]; ppcM4 += 2; } pMHAll = HostInit(ppcM4); if (fListOnly) { for (; (META_HOST *)0 != pMHAll; pMHAll = pMHAll->pMHnext) { printf("%s\n", pMHAll->pchost); } exit(0); } /* Get our m4 processing setup al-la distrib -E */ SetCurPhase("output"); if (-1 == (wM4 = PipeFit(pcM4Found, ppcM4, environ, 1))) { exit(EX_SOFTWARE); } if (!fExec) { register char *pc, *pcEq; register int iKind = '.'; for (ppc = ppcM4; (char *)0 != (pc = *ppc); ++ppc) { if ('-' == pc[0]) { iKind = pc[1]; continue; } switch (iKind) { case 'D': if ((char *)0 == (pcEq = strchr(pc, '='))) printf("define(`%s')dnl\n", pc); else printf("define(`%.*s',`%s')dnl\n", (int)(pcEq-pc), pc, pcEq+1); break; case 'I': printf("dnl add include path `%s'\n", pc); break; case 'U': printf("undefine(`%s')dnl\n", pc); break; case 'f': printf("include(`%s')dnl\n", pc); break; case '.': default: break; } iKind = 'f'; } printf("dnl-n set\n"); } RecurBanner(stdout); for (ppcPrep = util_ppm_size(pPPMTop, 0); (char *)0 != *ppcPrep; ++ppcPrep) { printf("%s\n", *ppcPrep); } for (; (META_HOST *)0 != pMHAll; pMHAll = pMHAll->pMHnext) { register int cc, fd; auto char acBuf[16384]; auto int wRecipe; if (-1 != pMHAll->uu) { fprintf(stdout, "pushdef(`%sU',%u)", pcXM4Define, (unsigned)pMHAll->uu); } fprintf(stdout, "pushdef(`%sB',%u)", pcXM4Define, pMHAll->udefs); ScanMacros(pMHAll->pMD, M4Pushdef, (void *)stdout); fprintf(stdout, "dnl\n"); for (i = 0; i < argc; ++i) { if (i < uLeftLits || i >= uRightBegin) { printf("%s\n", argv[i]); continue; } if ((char *)0 != ppcIsCache[i]) { register int fd; auto int aiFake[2]; (void)strcpy(acTemp, ppcIsCache[i]); if (-1 == (fd = mkstemp(acTemp))) { fprintf(stderr, "%s: mkstemp: %s: %s\n", progname, acTemp, strerror(errno)); exit(EX_OSERR); } close(fd); aiFake[0] = 1; aiFake[1] = 0; if (0 != (wRecipe = FillSlot(acTemp, &argv[i], ppcM4, pMHAll, aiFake, pcRecipe))) { fprintf(stderr, "%s: %s: m4 failed with %d\n", progname, argv[i], wRecipe); exit(wRecipe); } fprintf(stdout, "paste(`%s')syscmd(`rm -f %s')dnl\n", acTemp, acTemp); fflush(stdout); continue; } if (-1 == (fd = open(argv[i], O_RDONLY, 0000))) { close(1); fprintf(stderr, "%s: open: %s: %s\n", progname, argv[i], strerror(errno)); exit(EX_NOINPUT); } while (0 < (cc = read(fd, acBuf, sizeof(acBuf)))) { fwrite(acBuf, 1, cc, stdout); } close(fd); } if (-1 != pMHAll->uu) { fprintf(stdout, "popdef(`%sU')", pcXM4Define); } fprintf(stdout, "popdef(`%sB'", pcXM4Define); ScanMacros(pMHAll->pMD, M4ListMacro, (void *)stdout); fprintf(stdout, ")dnl\n"); } fflush(stdout); fclose(stdout); (void)open("/dev/null", O_WRONLY, 0000); wExit = EX_UNAVAILABLE << 8; while (-1 != (wWait = wait(&wExit))) { if (wWait == wM4) break; } /* Remove the temp files, they could be using the merged file so * wait until they exit. */ CleanSlot(pcHostSlot); (void)rmdir(acMySpace); exit(WEXITSTATUS(wExit)); } %%