#!mkcmd # $Id: mmsrc.m,v 1.69 2012/08/30 00:24:53 ksb Exp $ # Bootstrap code to get msrc working on a new master source host (ksb) # # You'll need the C code this program builds (from mkcmd) to even build # mkcmd. Which I know is odd, but that's the way it is. This code # produces the program "mmsrc.c" which is the 2008 version of msrc0. # Compiled on a target host it gets you enough spell points to get going. # # We run the equiv of msrc -l for you, only for this single host, w/o # any loop-back network connections. To do that we can use a distrib # configuration or a -DHOST= this host by name or `localhost'. -- ksb # # $Compile: ${mkcmd:-mkcmd} -I../msrc -I../hxmd -G -n mmsrc %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 "mmsrc" "" from '"config.h"' from '"machine.h"' require "util_errno.m" "util_ppm.m" require "std_help.m" "std_version.m" require "evector.m" "hostdb.m" "slot.m" require "envfrom.m" "make.m" getenv ; char* named "pcETmp" { local hidden } init 8 'if ((char *)0 != (pcETmp = getenv(acMMEnv)) && \'\\000\' != *pcETmp) {u_envopt(pcETmp, & argc, & argv);unsetenv(acMMEnv);} else if ((char *)0 != (pcETmp = getenv("MMSRC"))) {u_envopt(pcETmp, & argc, & argv);}' augment action 'V' { user "Version();" } augment char* 'm' { key 1 { '"__msrc"' # use the same default -m token, not __mmsrc } } %i static char rcsid[] = "$Id: mmsrc.m,v 1.69 2012/08/30 00:24:53 ksb Exp $"; #if !defined(DEF_HXMD_LIB) #define DEF_HXMD_LIB ".:/usr/local/lib/hxmd:/usr/local/lib/distrib" #endif #if !defined(DEF_MSRC_LIB) #define DEF_MSRC_LIB "/usr/local/lib/msrc:/usr/local/lib/hxmd" #endif static char acMMEnv[] = "MMSRC_PASS", acHxmdLib[] = "HXMD_LIB", acMsrcLib[] = "MSRC_LIB", acHxmdSearch[] = DEF_HXMD_LIB, /* default for $acHxmdLib */ acMsrcSearch[] = DEF_MSRC_LIB; /* default for $acMsrcLib */ static int fShowProvision = 0, fScriptX = 0, fDebug = 0, iFirstDiv = 1; static char acMySpace[] = "/tmp/ms02XXXXXX"; /* hxmd forces slot-0 to be +x, we don't */ #undef SLOT_0_ORBITS %% augment action "h" { user 'printf("%%s: also any database option to hxmd\\n", %b);' } 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" } action 'b' { track 'uGaveB' help "output the list of words in the given make macro" exclude 'DIUjuCBEGXZkoY' list { update "Msync(%#, %@);" aborts "exit(EX_OK);" param "macro" help "output words from each make macro" } update "/* in our list */" } char* 'd' { named "pcDebug" param "flags" user "DebugFilter(%n);" help "debug flags we pass on to m4 ('?' for help)" } # 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 "getuid() == 0" } after { hidden named "AfterGlow" update "%n(argc, argv);" help "check for re-exec cases" } boolean 'z' { named "fHXFlags" init "1" help "ignore HXINCLUDE from makefile" } list { named "List" param "utility" help "run utility in the platform directory" } init 5 "MmsrcStart();" # optiions we consume because we are being nice unsigned 'P' { hidden local named "iParallel" track "fGavePara" init "1" help "number of tasks to run in parallel, default %qi" param "[jobs]" noparam "if (%ypi == (%N = getenv(\"PARALLEL\")) || '\\000' == *%N) {%N = \"1\";}" after "if (%n < 1) {%n = 1;}" } boolean 'ls' { hidden named "fLocalForced" update "%n |= %w == 'l';" help "consumed to be more compatible with msrc" } before { update "/* used to move -l up to before AfterGlow */" after 'if (%rln) {u_argv_add(%ryn, "MODE=local");}' } function 'u' { named "SetEntryLogin" param 'login' help "shorthand to define ENTRY_LOGIN, for msrc compatibility" } %c static PPM_BUF PPMM4; static unsigned int uM4c = 0; static int fShowExec = 0, /* show (fork'd and) exec'd processes */ fShowRm = 0; /* show tmp space management */ /* cleanup the wrapper directory if it is empty (ksb) */ static void MmsrcExit() { if (0 == rmdir(acMySpace) && fShowRm) { fprintf(stderr, "%s: rmdir %s\n", progname, acMySpace); } TempNuke(); } /* Any init actions we need (ksb) * Strangely we can't masquerade as "msrc" when called "mmsrc", because * there might not be any zero configuration files installed (yet). * You'll have to explicitly rename us as "msrc" if you want that to * happen, or add "-Z msrc.cf" on the command-line (or in $MMSRC). */ static void MmsrcStart() { extern char *mkdtemp(char *); register char **ppc; auto char acTemp[MAXPATHLEN+4]; if ((char *)0 == mkdtemp(acMySpace)) { fprintf(stderr, "%s: mkdtemp: %s: %s\n", progname, acMySpace, strerror(errno)); exit(EX_OSERR); } if (fShowRm) { fprintf(stderr, "%s: mkdir %s\n", progname, acMySpace); } (void)atexit(MmsrcExit); if ((char *)0 == pcZeroConfig ? (0 == strcmp("-", progname) || 0 == strcmp("stdin", progname)) : (0 == strcmp("-", pcZeroConfig))) { pcZeroConfig = "-"; } else if ((char *)0 == pcZeroConfig && 0 != strcmp("mmsrc", progname)) { snprintf(acTemp, sizeof(acTemp)-1, "%s.cf", progname); pcZeroConfig = strdup(acTemp); } util_ppm_init(& PPMM4, sizeof(char *), 256); ppc = util_ppm_size(& PPMM4, 256); uM4c = 0; ppc[uM4c++] = "m4"; } static char *apcDefUtil[] = { "make", (char *)0 }; /* Add our voice to the version info, important because we can't be (ksb) * rebuilt until all of mkcmd, explode, install, ptbw, xclate, xapply, * hxmd, and msrc are built (that's 11 directories with lib's). */ static void Version() { register int i; printf("%s: makefile code: %s\n", progname, rcs_make); printf("%s: environment code: %s\n", progname, rcs_envfrom); 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); if ((char *)0 == pcMakefile) { printf("%s: no make recipe file\n", progname); } else { printf("%s: makefile: %s\n", progname, pcMakefile); } printf("%s: make hook: %s", progname, pcMakeHook); if ((char *)0 != pcMakeClean) printf(":%s", pcMakeClean); printf("\n%s: default utility:", progname); for (i = 0; i < sizeof(apcDefUtil)/sizeof(apcDefUtil[0])-1; ++i) { printf(" %s", apcDefUtil[i]); } printf("\n%s: temporary directory template: %.*sXXXXXX\n", progname, (int)strlen(acMySpace)-6, acMySpace); printf("%s: cache recipe file: %s\n", progname, acDownRecipe); printf("%s: default $%s: %s\n", progname, acHxmdLib, acHxmdSearch); printf("%s: default $%s: %s\n", progname, acMsrcLib, acMsrcSearch); } /* Remove tail component of the hostname until it matches. (ksb) * Eats the input string, and should use strcasecmp(3), really -- ksb */ static META_HOST * FindTarget(META_HOST *pMHList, char *pcWhole) { register char *pcTail; register META_HOST *pMH; pcTail = strlen(pcWhole)+pcWhole; do { *pcTail = '\000'; for (pMH = pMHList; (META_HOST *)0 != pMH; pMH = pMH->pMHnext) { if (0 == strcmp(pMH->pchost, pcWhole)) return pMH; } pcTail = strrchr(pcWhole, '.'); } while ((char *)0 != pcTail); return (META_HOST *)0; } #if !defined(HOST_NAME_MAX) #define HOST_NAME_MAX (MAXHOSTNAMELEN) #endif /* Find a likely name for myself in a list of meta hosts (ksb) * if there is one host in the matched set, that must be us; * (that is to say the invocation sepcified on the command line) * fetch gethostname and search for that name; * search for "localhost.domain.from.above" * else we must be the first host matched (since -Y/-G selected order) */ static META_HOST * FindMyself(META_HOST *pMHList) { register META_HOST *pMHRet; register char *pcDot; auto char acHostname[HOST_NAME_MAX], acFake[HOST_NAME_MAX]; if ((META_HOST *)0 == pMHList) { fprintf(stderr, "%s: no hosts selected, we can\'t find ourself in an empty list\n", progname); exit(EX_CONFIG); } if ((META_HOST *)0 == pMHList->pMHnext) { return pMHList; } pMHRet = (META_HOST *)0; snprintf(acFake, sizeof(acFake), "localhost"); if (-1 == gethostname(acHostname, sizeof(acHostname))) { /* just try localhost */ } else if ((META_HOST *)0 != (pMHRet = FindTarget(pMHList, acHostname))) { return pMHRet; } else if ((char *)0 != (pcDot = strchr(acHostname, '.'))) { snprintf(acFake, sizeof(acFake), "localhost.%s", pcDot+1); } if ((META_HOST *)0 == (pMHRet = FindTarget(pMHList, acFake))) { pMHRet = pMHList; } return pMHRet; } static char *pcMyName = (char *)0; /* Pass a single letter switch or switch with a param down to m4. (ksb) * copied from msrc's msrc.m, because we trap different options. */ static void AddSwitch(int iOpt, char *pcArg) { register char **ppc; /* Also tap -D option stream for some clues we need ... -- ksb * HOST= for the host selection, and INTO= for a different cache */ if ('D' == iOpt && (char *)0 != pcArg) { register unsigned u; register char *pcReal; pcReal = pcArg; if ('_' != *pcArg && !isalnum(*pcArg)) { ++pcReal; } if (0 == strncmp("HOST=", pcReal, 5) && '\000' != pcReal[5]) { pcMyName = pcArg+5; } 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; } /* Make mmsrc look like msrc for the -u option (ksb) */ static void SetEntryLogin(int iOpt, char *pcArg) { auto char acTemp[MAXPATHLEN+512]; if ('\000' == *pcArg) { return; } snprintf(acTemp, sizeof(acTemp), "ENTRY_LOGIN=%s", pcArg); AddSwitch('D', strdup(acTemp)); } /* Filter the -d options to snoop-out the flags we want to see (ksb) * same as msrc mostly trap 'P' and 'S' and 'X', I suppose 'R' as well. */ static void DebugFilter(const char *pcFlags) { if ((const char *)0 != pcFlags && (char *)0 != strchr(pcFlags, 'P')) { ++fShowProvision; ++fShowMake; } if ((char *)0 != strchr(pcDebug, 'S')) { ++fScriptX; ++fShowMake; } if ((char *)0 != strchr(pcDebug, 'X')) { ++fShowExec; ++fDebug; } if ((char *)0 != strchr(pcDebug, 'R')) { ++fShowRm; } } /* Shift the quote mode from `m4' (`), to [m4], to none ' ' (ksb) * Request '`' or '[' to get statered from ' ' mode, then * requesting ' ' ends the string (and returns the `default' quotes). * Normal reset pattern installed for piCur. [Copied from msrc.m.] */ static void QuoteMe(FILE *fpOut, int *piCur, int iWant) { static int fDirty = 0; if ((int *)0 == piCur) { fDirty = 0; return; } if (*piCur == iWant) { return; } switch (iWant) { case ' ': if ('`' == *piCur) { fputc('\'', fpOut); break; } if ('[' == *piCur) { fprintf(fpOut, "]dnl\nchangequote(`,')dnl\n"); break; } break; case '`': if (' ' == *piCur) { fputc('`', fpOut); break; } if ('[' == *piCur) { fprintf(fpOut, "]dnl\nchangequote(`,')dnl\n`"); break; } break; case '[': if (' ' == *piCur) { if (fDirty) { fprintf(fpOut, "`'"); } fprintf(fpOut, "changequote([,])dnl\n["); break; } if ('`' == *piCur) { fprintf(fpOut, "'dnl\nchangequote([,])dnl\n["); break; } break; default: fprintf(stderr, "%s: %c: unknown internal quote mode\n", progname, iWant); exit(EX_SOFTWARE); } *piCur = iWant; fDirty = 1; } /* Output a given word inside shell double quotes (ksb) * sh -c "the local word's we speak" */ static void LocalWord(FILE *fpOut, int *piMode, const char *pcScan) { static char acLocalBack[] = "\\"; register int c; for (/* above */;'\000' != (c = *pcScan); ++pcScan) { switch (c) { case '\\': /* shell special, backslash each */ case '$': case '\"': fprintf(fpOut, "%s%c", acLocalBack, c); break; case '`': /* shell and m4 special */ QuoteMe(fpOut, piMode, '['); fprintf(fpOut, "%s%c", acLocalBack, c); break; case '\'': /* just m4 special */ QuoteMe(fpOut, piMode, '['); fprintf(fpOut, "%c", c); break; case '[': /* might be in the other quotes */ case ']': QuoteMe(fpOut, piMode, ' '); fprintf(fpOut, "%c", c); break; default: /* anything else is literal in shell doubles */ putc(c, fpOut); break; } } } /* Make sure any required subdir of the target is made (ksb) */ static void LocalDir(FILE *fpOut, BOOTY *pBO, const char *pcTail, int *piMode, PPM_BUF *pPPMList, unsigned *puDx) { register char **ppcUniq, *pcThis, *pcDir; register size_t wLen; while ('/' == *pcTail) { ++pcTail; } wLen = (strlen(pBO->pcinto)+1+strlen(pcTail))|7; wLen += 1; if ((char *)0 == (pcThis = malloc(wLen))) { fprintf(stderr, "%s: malloc: %s\n", progname, strerror(errno)); exit(EX_OSERR); } *pcThis = '\000'; for (pcDir = (char *)pcTail; (char *)0 != (pcDir = strchr(pcDir, '/')); ++pcDir) { snprintf(pcThis, wLen-1, "%s/%.*s", pBO->pcinto, (int)(pcDir-pcTail), pcTail); ppcUniq = util_ppm_size(pPPMList, (*puDx|7)+1); ppcUniq[*puDx] = (char *)0; if (InList(ppcUniq, pcThis)) { *pcThis = '\000'; continue; } ppcUniq[(*puDx)++] = strdup(pcThis); } if ('\000' == *pcThis) { return; } fprintf(fpOut, "mkdir -p "); LocalWord(fpOut, piMode, pcThis); fprintf(fpOut, "\n"); } /* Make the remote directory look like the booty says it should (ksb) * Copy the files in like msrc -l does, if we need to (don't update * files that are the same, so make(1) works. -- ksb */ static void Lsync(FILE *fpOut, BOOTY *pBO, const char *pcM4ed, char **argv, META_HOST *pMHOnly) { register int i; register const char **ppcCur, *pcSep; auto int iMode; auto PPM_BUF PPMList; auto unsigned uDx; static char acNoSlot[] = "\000"; fprintf(fpOut, "dnl mmsrc local update script\n`#!/bin/sh\n'"); SlotDefs(fpOut, acNoSlot); HostDefs(fpOut, pMHOnly); fprintf(fpOut, "dnl shell functions\ndivert(%d)dnl\n", iFirstDiv); iMode = ' '; fprintf(fpOut, "`set _ \"%s\" \"%s\" \"%s\" \"'ifdef(`ENTRY_LOGIN',`ENTRY_LOGIN'@)HOST\"` \"%s\"; shift'\n", pBO->pcinto, pcMakefile, pBO->pcmode, pBO->pcinto); fprintf(fpOut, "ifdef(`INCLUDE_CMD',`INCLUDE_CMD(`%s')\n')dnl\ndivert(%d)dnl\n", pBO->pcmode, iFirstDiv+2); fprintf(fpOut, "dnl before INIT\ndivert(%d)dnl\nifdef(`INIT_CMD',`INIT_CMD\n')dnl\n", iFirstDiv+4); if ((const char **)0 != (ppcCur = (const char **)pBO->ppcsubdir)) { QuoteMe(fpOut, &iMode, '`'); for (i = 0; (char *)0 != ppcCur[i]; ++i) { fprintf(fpOut, "mkdir -p "); LocalWord(fpOut, & iMode, pBO->pcinto); fprintf(fpOut, "/"); LocalWord(fpOut, & iMode, ppcCur[i]); fprintf(fpOut, " || exit 73\n"); } } util_ppm_init(& PPMList, sizeof(char *), 256); uDx = 0; if ((const char **)0 != (ppcCur = (const char **)pBO->ppcsend)) { QuoteMe(fpOut, &iMode, '`'); for (i = 0; (char *)0 != ppcCur[i]; ++i) { if (InList(pBO->ppcmap, ppcCur[i])) continue; LocalDir(fpOut, pBO, pBO->ppctarg[i], & iMode, & PPMList, & uDx); fprintf(fpOut, "cmp -s %s", '-' == ppcCur[i][0] ? "-- " : ""); LocalWord(fpOut, & iMode, ppcCur[i]); fprintf(fpOut, " "); LocalWord(fpOut, & iMode, pBO->pcinto); fprintf(fpOut, "/"); LocalWord(fpOut, & iMode, pBO->ppctarg[i]); fprintf(fpOut, " || cp -f -p %s", '-' == ppcCur[i][0] ? "-- " : ""); LocalWord(fpOut, & iMode, ppcCur[i]); fprintf(fpOut, " "); LocalWord(fpOut, & iMode, pBO->pcinto); fprintf(fpOut, "/"); LocalWord(fpOut, & iMode, pBO->ppctarg[i]); fprintf(fpOut, "\n"); } } if ((const char **)0 != (ppcCur = (const char **)pBO->ppcmap)) { QuoteMe(fpOut, &iMode, '`'); for (i = 0; (char *)0 != ppcCur[i]; ++i) { LocalDir(fpOut, pBO, ppcCur[i], & iMode, & PPMList, & uDx); fprintf(fpOut, "cmp -s %s", '-' == pcM4ed[0] ? "-- " : ""); LocalWord(fpOut, & iMode, pcM4ed); fprintf(fpOut, " "); LocalWord(fpOut, & iMode, pBO->pcinto); fprintf(fpOut, "/"); LocalWord(fpOut, & iMode, ppcCur[i]); fprintf(fpOut, " || cp -f -p %s", '-' == pcM4ed[0] ? "-- " : ""); LocalWord(fpOut, & iMode, pcM4ed); fprintf(fpOut, " "); LocalWord(fpOut, & iMode, pBO->pcinto); fprintf(fpOut, "/"); LocalWord(fpOut, & iMode, ppcCur[i]); fprintf(fpOut, "\n"); pcM4ed += strlen(pcM4ed)+1; } } QuoteMe(fpOut, &iMode, ' '); fprintf(fpOut, "dnl before PRE\ndivert(%d)dnl\nifdef(`PRE_CMD',`PRE_CMD\n')dnl\n", iFirstDiv+6); fprintf(fpOut, "ifdef(`ENTRY_DEFS',`. defn(`ENTRY_DEFS')"); /* Like all wrappers we take ":" for an internal no-op */ if (':' == argv[0][0] && '\000' == argv[0][1] && (char *)0 == argv[1]) { /* internal no-op: don't do anything at all */ fprintf(fpOut, "')\n"); } else { fprintf(fpOut, " && ')$SHELL -c \"cd "); QuoteMe(fpOut, &iMode, '`'); LocalWord(fpOut, & iMode, pBO->pcinto); fprintf(fpOut, "\n"); pcSep = ""; while ((char *)0 != *argv) { LocalWord(fpOut, & iMode, pcSep); LocalWord(fpOut, & iMode, *argv++); pcSep = " "; } QuoteMe(fpOut, &iMode, ' '); fprintf(fpOut, "\"\n"); } QuoteMe(fpOut, &iMode, ' '); fprintf(fpOut, "dnl before POST\ndivert(%d)dnl\nifdef(`POST_CMD',`POST_CMD\n')dnl\n", iFirstDiv+8); } static BOOTY BO; extern char **environ; /* Default missing data, debug level, plunder the makefile (ksb) * if ppchxflags holds a config file option we have to add * it to our list, tag the fact we did it and re-exec ourself. */ static void AfterGlow(int argc, char **argv) { register const char *pcCat; auto PPM_BUF PPMExec; register int i, wOuch; register char **ppcExec; if ((char *)0 != pcDebug) { AddSwitch('d', pcDebug); } else { pcDebug = ""; } if ((char *)0 != strchr(pcDebug, '?')) { printf("%s: -d [PRS?]*[aceflqtxV]*\nP\ttrace makefile provisions\nR\ttrace temporary file creation/removal\nS\ttrace shell actions\nX\ttrace execution of subcommands\n?\tdisplay these options and exit\nall others are provided by m4\n", progname); exit(0); } if ((char *)0 == pcMakefile || '\000' == *pcMakefile) { fprintf(stderr, "%s: no make recipe file found\n", progname); exit(EX_DATAERR); } if (uGaveB) { return; } /* Plunder the proposed makefile, strangely it might not be * the same makefile we are going to read later ... --ksb */ if (0 != Booty(& BO, pcMakefile, fShowProvision ? stdout : (FILE *)0)) { fprintf(stderr, "%s: %s: cannot read macro values\n", progname, pcMakefile); exit(EX_CONFIG); } /* Set $MMSRC_PASS if HXINCLUDE is not empty or the string "--", * we also look for the lib/mmsrc.hxmd file, like msrc does. * Then re-exec ourself with -z. We are a self-wrapper, kinda. */ if (0 == fHXFlags) { return; } if ((const char *)0 == (pcCat = EnvFromFile(BO.ppchxinclude, BO.uhxinclude))) { static const char acSuffix[] = ".hxmd"; register const char *pcSPath; register char *pcComp; auto char *apcLook[2]; auto char acTry[MAXPATHLEN+4]; if ((char *)0 == (pcSPath = getenv(acMsrcLib)) || '\000' == pcSPath[0]) { pcSPath = acMsrcSearch; } snprintf(acTry, sizeof(acTry), "%s%s", progname, acSuffix); apcLook[0] = acTry; apcLook[1] = (char *)0; if ((char *)0 != (pcComp = util_fsearch(apcLook, pcSPath))) { apcLook[0] = pcComp; pcCat = EnvFromFile(apcLook, 1); if (fDebug) { fprintf(stderr, "%s: read %s\n", progname, pcComp); } } } if ((char *)0 == pcCat || ('-' == pcCat[0] && '-' == pcCat[1] && ('\000' == pcCat[2] || (' ' == pcCat[2] && '\000' == pcCat[3])))) { return; } if (fDebug) { fprintf(stderr, "%s: $%s=%s\n", progname, acMMEnv, pcCat); } setenv(acMMEnv, pcCat, 1); util_ppm_init(& PPMExec, sizeof(char *), 256); ppcExec = util_ppm_size(& PPMExec, ((argc+2)|7)+1); i = 0; ppcExec[i++] = argv[0]; ppcExec[i++] = "-z"; while (argc-- > 0) { ppcExec[i++] = *++argv; } ppcExec[i] = (char *)0; fflush(stdout); if (fShowExec) { fprintf(stderr, "%s: execve:", progname); for (i = 0; (char *)0 != ppcExec[i]; ++i) { fprintf(stderr, " %s", ppcExec[i]); } fprintf(stderr, "\n"); } fflush(stderr); execvp(ppcExec[0], ppcExec); wOuch = errno; execve(ppcExec[0], ppcExec, environ); fprintf(stderr, "%s: execvp: %s: %s\n", progname, ppcExec[0], strerror(wOuch)); exit(EX_UNAVAILABLE); /*NOTREACHED*/ } /* Msync needs to know the value of $SOURCE and maybe $GEN from the (ksb) * make recipe, but make -V doesn't exist on Linux (just *BSD). Emmulate it. */ static void Msync(int argc, char **argv) { static MAKE_VAR aMVMsync[] = { {""}, /* copy these from this directory */ {(char *)0} }; register char **ppc; if (!uGaveReq) { pcMakeHook = "__msync"; } while (argc-- > 0) { aMVMsync[0].pcname = *argv++; if (EX_OK != GatherMake(pcMakefile, aMVMsync)) { fprintf(stderr, "%s: -b: %s: target failed\n", progname, pcMakeHook); exit(EX_DATAERR); } if ((char **)0 != (ppc = GetMVValue(& aMVMsync[0]))) { for ( ; (char *)0 != *ppc; ++ppc) { printf("%s\n", *ppc); } } } exit(EX_OK); } /* We build a directory (INTO) to cache a configured copy of (ksb) * the msrc spool in this directory (fake msrc -l), then * exec the remainder of argv to take some action. We trap -DHOST * to "auto config" because we are used to build the more robust * tool chain before the Admin has any domain.cf built. */ static void List(int argc, char **argv) { register int i; auto char *pcHostSlot, *pcSlot, **ppcM4, **ppc; auto const char *pcCacheCtl; auto struct stat stPwd, stInto; auto int wExit, *piIsCache; auto pid_t wM4, wWait; auto unsigned int uSlotLen; auto FILE *fpL; auto META_HOST *pMHMyself, *pMHAll; auto struct stat stExec; /* Build the M4 phase buffer for HXMD_PHASE=(selection|%d|cmd), */ AddSwitch('D', SetupPhase("PHASE", 64)); /* Put the fixed m4 prep files in the m4 template. * 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', "-"); if (0 == argc || (char **)0 == argv) { argv = apcDefUtil; argc = sizeof(apcDefUtil)/sizeof(apcDefUtil[0]) - 1; } if (-1 == stat(".", & stPwd)) { fprintf(stderr, "%s: stat: .: %s\n", progname, strerror(errno)); exit(EX_IOERR); } /* Read configuration data, first the -C/-X/-Z files, then the * local make(1) file for macro values and prereqs (like msrc does). * Then force INTO from any -D on the command line. * Find the cache control slot from hostdb.m, or fail. */ if ((char *)0 == (pcHostSlot = HostSlot(acMySpace))) { exit(EX_DATAERR); } pcCacheCtl = FindCache("make", pcHostSlot); ppcM4 = util_ppm_size(& PPMM4, 0); pMHAll = HostInit(ppcM4); /* Find our hostname in the provided DB, -DHOST=name else guess. */ if ((char *)0 != pcMyName) { pMHMyself = FindTarget(pMHAll, strdup(pcMyName)); if ((META_HOST *)0 == pMHMyself) { fprintf(stderr, "%s: %s: no such host defined\n", progname, pcMyName); exit(EX_NOHOST); } } else { pMHMyself = FindMyself(pMHAll); if ((META_HOST *)0 == pMHMyself) { fprintf(stderr, "%s: no hosts defined\n", progname); exit(EX_NOHOST); } } /* Inv. BO.pcinto is not empty or nil, so make sure it is not an alias * for the current working directory. That would cause us to clobber * our source files, and that would be Poor Form. --ksb */ if (-1 != stat(BO.pcinto, & stInto)) { if (stPwd.st_dev == stInto.st_dev && stPwd.st_ino == stInto.st_ino) { fprintf(stderr, "%s: INTO cannot specify the current working directory, use a parallel space\n", progname); exit(EX_PROTOCOL); } } else if (-1 == mkdir(BO.pcinto, (07777 & stPwd.st_mode))) { /* We don't (and shan't) mkdir the enclosing directory -- ksb */ fprintf(stderr, "%s: mkdir: %s: %s\n", progname, BO.pcinto, strerror(errno)); exit(EX_NOPERM); } else { (void)chmod(BO.pcinto, 0700 | (07777 & stPwd.st_mode)); } if ((char *)0 == BO.pcmode || 'a' == BO.pcmode[0] || 'A' == BO.pcmode[0]) { /* don't look */ } else if ('l' != BO.pcmode[0] && 'L' != BO.pcmode[0]) { fprintf(stderr, "%s: %s: requests \"%s\" mode, but \"local\" is all that is supported\n", progname, pcMakefile, BO.pcmode); exit(EX_UNAVAILABLE); } BO.pcmode = "local"; /* Get our m4 processing setup al-la hxmd, do the local sync, * change directory to that cache remove the temp files and * run (exec) the command we wrap. */ if ((char *)0 == (pcSlot = malloc(sizeof(char)*BO.uorig*(MAXPATHLEN+4)))) { fprintf(stderr, "%s: malloc: %lu: %s\n", progname, (unsigned long)(sizeof(char)*BO.uorig*(MAXPATHLEN+4)), strerror(errno)); exit(EX_OSERR); } if ((char **)0 == BO.ppcorig) { static char *pcSentinel; pcSentinel = (char *)0; BO.ppcorig = & pcSentinel; } uSlotLen = InitSlot("XXXXXX", pcSlot, BO.ppcorig, acMySpace); if ((int *)0 == (piIsCache = calloc((uSlotLen|3)+1, sizeof(int)))) { fprintf(stderr, "%s: calloc: %s\n", progname, strerror(errno)); exit(EX_OSERR); } /* do that same checks hxmd does for our files, don't process stdin */ for (i = 0; i < BO.uorig; ++i) { register int iOK; auto struct stat stCheck; auto char *pcFound, *apcLook[2]; auto char acRecipe[MAXPATHLEN+4]; piIsCache[i] = 0; if ('-' == BO.ppcorig[i][0] && '\000' == BO.ppcorig[i][1]) { fprintf(stderr, "%s: MAP: -: cannot process stdin (yet)\n", progname); exit(EX_SOFTWARE); } apcLook[0] = BO.ppcorig[i]; apcLook[1] = (char *)0; if ('.' == BO.ppcorig[i][0] && '/' == BO.ppcorig[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, BO.ppcorig[i]); } else { BO.ppcorig[i] = pcFound; } if (0 != stat(BO.ppcorig[i], & stCheck)) { fprintf(stderr, "%s: stat: %s: %s\n", progname, BO.ppcorig[i], strerror(errno)); exit(EX_NOINPUT); } if (S_ISDIR(stCheck.st_mode)) { snprintf(acRecipe, sizeof(acRecipe), "%s/%s", BO.ppcorig[i], acDownRecipe); if ((char *)0 == (BO.ppcorig[i] = strdup(acRecipe))) { fprintf(stderr, "%s: strdup: %s\n", progname, strerror(errno)); exit(EX_TEMPFAIL); } if (0 != stat(BO.ppcorig[i], & stCheck)) { fprintf(stderr, "%s: stat: %s: %s\n", progname, BO.ppcorig[i], strerror(errno)); exit(EX_NOINPUT); } piIsCache[i] = 1; } if (S_ISFIFO(stCheck.st_mode)) { /* hxmd allows it */ } else if (!S_ISREG(stCheck.st_mode)) { fprintf(stderr, "%s: %s: must be a plain file, or FIFO\n", progname, BO.ppcorig[i]); exit(EX_PROTOCOL); } else if (-1 == (iOK = open(BO.ppcorig[i], O_RDONLY, 0666))) { fprintf(stderr, "%s: open: %s: %s\n", progname, BO.ppcorig[i], strerror(errno)); exit(EX_OSERR); } else { close(iOK); } } if (0 == (wWait = FillSlot(pcSlot, BO.ppcorig, ppcM4, pMHMyself, piIsCache, pcCacheCtl))) { /* nada */ } else if (WIFEXITED(wWait)) { fprintf(stderr, "%s: %s: %s: exits code %d\n", progname, pMHMyself->pchost, ppcM4[0], WEXITSTATUS(wWait)); exit(EX_NOHOST); } else { fprintf(stderr, "%s: %s: %s: caught signal %d\n", progname, pMHMyself->pchost, ppcM4[0], WTERMSIG(wWait)); exit(EX_NOHOST); } stExec.st_mode = 0700; stExec.st_uid = getuid(); stExec.st_gid = getgid(); SetCurPhase("cmd"); if ((FILE *)0 == (fpL = M4Gadget(pcHostSlot, &stExec, &wM4, ppcM4))) { fprintf(stderr, "%s: fopen: %s: %s\n", progname, pcHostSlot, strerror(errno)); exit(EX_OSERR); } if (fScriptX) { Lsync(stderr, & BO, pcSlot, argv, pMHMyself); } Lsync(fpL, & BO, pcSlot, argv, pMHMyself); fclose(fpL); SetCurPhase((char *)0); wExit = EX_UNAVAILABLE << 8; while (-1 != (wWait = wait(&wExit))) { if (wWait == wM4) break; } system(pcHostSlot); CleanSlot(pcSlot); CleanSlot(pcHostSlot); if ((char *)0 == pcMakeClean) { exit(WEXITSTATUS(wExit)); } { auto char acTemp[MAXPATHLEN+256]; /* Under a clean target you get the exit code from make, but * make gets the _raw_ exit from the hxmd run. This lets you * check for a signal or whatever too. -- ksb * * make -s -f makefile words_in(pcMakeClean) (char *)0 */ snprintf(acTemp, sizeof(acTemp), "-DMSRC_EXITCODE=%d", wExit); i = 5 + WordScan(pcMakeClean, WordAdd, (char **)0); if ((char **)0 == (ppc = calloc((i|7)+9, sizeof(char *)))) { fprintf(stderr, "%s: out of memory\n", progname); exit(EX_OSERR); } i = 0; ppc[i++] = "make"; ppc[i++] = "-s"; /* should be able to defeat this */ ppc[i++] = "-f"; ppc[i++] = pcMakefile; ppc[i++] = acTemp; i += WordScan(pcMakeClean, WordAdd, ppc+i); ppc[i] = (char *)0; MmsrcExit(); ExecMake(ppc); } /* Should not be reached, ever --ksb */ (void)PrintSlot; exit(wExit); } %%