do not edit — generated by btf.
git.druid.rocksindexdruid520mpsrc/runsh.c

src/runsh.c


#define _POSIX_C_SOURCE 200809L
#define _NETBSD_SOURCE 1
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
 
#include "mpx.h"
 
void
cmd_runsh(int argc, char** argv)
{
	pid_t pid;
	int status;
	const char* shell;
	static char buf[MAXFILE];
	static char expbuf[MAXFILE];
	static char secbuf[MAXFILE];
	/* static, not stack: MAXKV * (MAXKEY+MAXVAL) is ~139KB -- the only
	 * frame in mpx that would otherwise breach the build's
	 * -Wstack-usage/-Wframe-larger-than limits. cmd_runsh runs at most
	 * once per process (mpx.c main dispatches straight into it) and
	 * parseconf fully initializes every entry it returns, so static
	 * storage is safe. */
	static struct kv kv[MAXKV];
	long n;
	long expn;
	long secn;
	long nkv;
	const char* pkgname;
	const char* target;
	const char* cc;
	const char* ar;
	const char* ranlib;
	const char* strp;
	const char* host;
	const char* sysroot;
	char sysrootflag[MAXVAL] = {0};
	const char* arch;
	const char* microarch;
	char archflag[MAXVAL] = {0};
	const char* prefix;
	char prefixflag[MAXVAL] = {0};
	const char* depprefixes;
	const char* jobs;
	long njobs;
	char jobsflag[MAXVAL];
	char njobsbuf[32];
	const char* sandbox;
 
	if(argc < 4)
	{
		badusage(argv[0], "runsh <dir> <script> [NAME=VAL ...]");
	}
	shell = "/bin/sh";
	target = "native";
	cc = NULL;
	ar = NULL;
	ranlib = NULL;
	strp = NULL;
	host = NULL;
	sysroot = NULL;
	arch = NULL;
	microarch = NULL;
	jobs = NULL;
	sandbox = NULL;
	pkgname = argval(argc, argv, "MP_PKG");
	{
		/* MP_MPXCONF (set by mplib::hooks::port_env_args only while a
		 * with_sysroot_scope callback is active) lets a sysroot have its
		 * own TARGET_CC/JOBS/MICROARCH/SANDBOX overrides distinct from
		 * the default root's -- every other WD-derived concept (db,
		 * manifest, hooks dir) already gets this same per-sysroot
		 * isolation; this file was the one config source still silently
		 * shared by every sysroot and the default root alike. absent
		 * (the default-root case, unchanged from before) or naming a
		 * file that doesn't exist yet (a sysroot with no override of its
		 * own), falls back to the single global /etc/mpx.conf exactly
		 * as always. */
		const char* mpxconf = argval(argc, argv, "MP_MPXCONF");
		if(mpxconf == NULL || mpxconf[0] == '\0')
		{
			mpxconf = "/etc/mpx.conf";
		}
		n = readfile(mpxconf, buf, (long)sizeof(buf) - 1);
		if(n == -1 && strcmp(mpxconf, "/etc/mpx.conf") != 0)
		{
			n = readfile("/etc/mpx.conf", buf, (long)sizeof(buf) - 1);
		}
	}
	if(n == -2)
	{
		/* unlike a missing mpx.conf (n == -1, silently optional --
		 * plenty of setups have no per-package overrides at all), a
		 * truncated read must not be silently treated the same way: it
		 * would silently drop whatever TARGET_/JOBS/sandbox overrides
		 * fall past the cut, for every package whose section comes
		 * after that point, with zero diagnostic. */
		die("mpx.conf is too large to read (increase MAXFILE)");
	}
	if(n >= 0)
	{
		/* IMPORT=<path1> <path2> ... (see conf.c's expand_imports): lets
		 * a large, complex mpx.conf be split across multiple files
		 * instead of growing one file without bound. a no-op (buf used
		 * as-is) when there's nothing to import. */
		expn = expand_imports(buf, n, expbuf, sizeof(expbuf));
		if(expn > 0)
		{
			memcpy(buf, expbuf, (size_t)expn);
			n = expn;
		}
	}
	if(n >= 0)
	{
		/* a per-package "<name>:" section in mpx.conf overrides any of
		 * these same keys globally, exactly like mp.conf's per-package
		 * sections -- extractsection folds pkgname's section (if any)
		 * and the global lines into one buffer, section first so
		 * kvget's first-match-wins picks the override. */
		secn = extractsection(buf, n, pkgname, secbuf, (long)sizeof(secbuf));
		nkv = parseconf(secbuf, secn, kv, MAXKV);
		if(kvget(kv, nkv, "SHELL") != NULL && kvget(kv, nkv, "SHELL")[0] != '\0'
		    && strlen(kvget(kv, nkv, "SHELL")) < (size_t)MAXPATH)
		{
			shell = kvget(kv, nkv, "SHELL");
		}
		if(kvget(kv, nkv, "TARGET") != NULL && kvget(kv, nkv, "TARGET")[0] != '\0')
		{
			target = kvget(kv, nkv, "TARGET");
		}
		cc = kvget(kv, nkv, "TARGET_CC");
		ar = kvget(kv, nkv, "TARGET_AR");
		ranlib = kvget(kv, nkv, "TARGET_RANLIB");
		strp = kvget(kv, nkv, "TARGET_STRIP");
		host = kvget(kv, nkv, "TARGET_HOST");
		sysroot = kvget(kv, nkv, "TARGET_SYSROOT");
		arch = kvget(kv, nkv, "ARCH");
		microarch = kvget(kv, nkv, "MICROARCH");
		jobs = kvget(kv, nkv, "JOBS");
		sandbox = kvget(kv, nkv, "SANDBOX");
	}
	/* a MP_SANDBOX=yes/no trailing arg (set by mp itself for the fetch
	 * phase and for legacy ports, which need real network) overrides
	 * whatever mpx.conf says -- mpx has no other way to know those. */
	if(argval(argc, argv, "MP_SANDBOX") != NULL)
	{
		sandbox = argval(argc, argv, "MP_SANDBOX");
	}
	/* only ever read inside "#ifdef __linux__" below (sandboxing is a
	 * no-op on other platforms); this keeps a non-Linux build warning
	 * free without scattering more ifdefs through the config reads. */
	(void)sandbox;
	njobs = 0;
	if(jobs != NULL && jobs[0] != '\0')
	{
		njobs = atol(jobs);
	}
#ifdef _SC_NPROCESSORS_ONLN
	if(njobs <= 0)
	{
		njobs = (long)sysconf(_SC_NPROCESSORS_ONLN);
	}
#endif
	if(njobs <= 0)
	{
		njobs = 1;
	}
	pid = fork();
	if(pid < 0)
	{
		die("fork failed");
	}
	if(pid == 0)
	{
		if(chdir(argv[2]) != 0)
		{
			_exit(1);
		}
		setenv("MP_TARGET", target, 1);
		setxenv("CC", cc);
		setxenv("AR", ar);
		setxenv("RANLIB", ranlib);
		setxenv("STRIP", strp);
		snprintf(njobsbuf, sizeof(njobsbuf), "%ld", njobs);
		setenv("MP_JOBS", njobsbuf, 1);
		snprintf(jobsflag, sizeof(jobsflag), "-j%ld", njobs);
		setflags("MAKEFLAGS", jobsflag);
		setxenv("MP_HOST", host);
		if(sysroot != NULL && sysroot[0] != '\0')
		{
			size_t off = 0;
 
			if(bufcat(sysrootflag, sizeof(sysrootflag), &off, "--sysroot=") == 0
			    || bufcat(sysrootflag, sizeof(sysrootflag), &off, sysroot) == 0)
			{
				_exit(1);
			}
			setenv("MP_SYSROOT", sysroot, 1);
			setflags("CFLAGS", sysrootflag);
			/* CPPFLAGS too, not just CFLAGS: autoconf's own AC_PROG_CPP
			 * sanity check (and anything else invoking $CPP/$CC -E
			 * directly) reads CPPFLAGS specifically, never CFLAGS - a
			 * --sysroot affects header search exactly the same way a
			 * plain -I does, so it needs to reach that probe too. */
			setflags("CPPFLAGS", sysrootflag);
			setflags("LDFLAGS", sysrootflag);
			/* qemu-user's own documented env var for "where to find the
			 * target's dynamic linker/shared libs" (identical to its -L
			 * flag) -- lets a check: phase's cross-built test binaries,
			 * if the host has qemu-user-binfmt registered for the target
			 * architecture (a host prerequisite mpx does not manage
			 * itself, the same way a host-provided git/shell already
			 * are), run transparently under emulation and still find
			 * their real target libraries instead of the host's. a
			 * no-op for anything not actually routed through qemu. */
			setenv("QEMU_LD_PREFIX", sysroot, 1);
		}
		setxenv("MP_ARCH", arch);
		if(microarch != NULL && microarch[0] != '\0')
		{
			size_t off = 0;
 
			if(bufcat(archflag, sizeof(archflag), &off, "-march=") == 0
			    || bufcat(archflag, sizeof(archflag), &off, microarch) == 0)
			{
				_exit(1);
			}
			setenv("MP_MICROARCH", microarch, 1);
			setflags("CFLAGS", archflag);
		}
		/* every mp-managed dependency lands under MP_PREFIX (or, for a
		 * slotted build, MP_PREFIX_BASE -- the pre-slot prefix other
		 * packages still install flatly into), which a build's own
		 * ./configure has no way to know to search: it only receives
		 * --prefix, telling it where to *install*, not where to find
		 * already-installed headers/libs. without this, a build can
		 * silently fail to detect a real, already-installed dependency
		 * and either error out or silently degrade. */
		prefix = argval(argc, argv, "MP_PREFIX_BASE");
		if(prefix == NULL)
		{
			prefix = argval(argc, argv, "MP_PREFIX");
		}
		if(prefix == NULL)
		{
			prefix = getenv("MP_PREFIX");
		}
		if(prefix != NULL && prefix[0] != '\0')
		{
			/* an rpath here matters whenever MP_PREFIX isn't already on
			 * the dynamic linker's default search path -- true for the
			 * common case (a real /usr/local-ish prefix, usually already
			 * configured system-wide), but NOT for a sysroot's own
			 * MP_PREFIX (e.g. /var/mp-sysroots/name), which is exactly
			 * the kind of non-standard path two unslotted packages
			 * within the SAME sysroot need to find each other's shared
			 * libs at runtime without it. matches the rpath the
			 * MP_DEP_PREFIXES loop below already adds per slotted
			 * dependency -- this is the same thing for the base prefix
			 * itself. */
			size_t off = 0;
 
			if(bufcat(prefixflag, sizeof(prefixflag), &off, "-I") == 0
			    || bufcat(prefixflag, sizeof(prefixflag), &off, prefix) == 0
			    || bufcat(prefixflag, sizeof(prefixflag), &off, "/include") == 0)
			{
				_exit(1);
			}
			setflags("CFLAGS", prefixflag);
			/* CPPFLAGS too - see the --sysroot comment above for why:
			 * autoconf's AC_PROG_CPP (and anything else compiling a
			 * probe with a bare "$CC -E") reads CPPFLAGS, not CFLAGS,
			 * so a build whose ./configure never finds MP_PREFIX's own
			 * headers without this -I explicitly present here too. */
			setflags("CPPFLAGS", prefixflag);
			off = 0;
			if(bufcat(prefixflag, sizeof(prefixflag), &off, "-L") == 0
			    || bufcat(prefixflag, sizeof(prefixflag), &off, prefix) == 0
			    || bufcat(prefixflag, sizeof(prefixflag), &off, "/lib -Wl,-rpath,") == 0
			    || bufcat(prefixflag, sizeof(prefixflag), &off, prefix) == 0
			    || bufcat(prefixflag, sizeof(prefixflag), &off, "/lib") == 0)
			{
				_exit(1);
			}
			setflags("LDFLAGS", prefixflag);
		}
		/* buildlink-style wiring: one -I/-L/rpath/PKG_CONFIG_PATH entry
		 * per colon-separated slotted-dependency prefix mp resolved for
		 * this package (mplib::resolve::dep_slot_prefixes), so a build
		 * links against exactly the dependency variant it declared
		 * instead of whatever else sits on the default search path. */
		depprefixes = argval(argc, argv, "MP_DEP_PREFIXES");
		if(depprefixes != NULL && depprefixes[0] != '\0')
		{
			char depbuf[MAXVAL];
			char depflag[MAXVAL] = {0};
			char* save;
			char* tok;
 
			{
				size_t dn = strlen(depprefixes);
 
				if(dn > sizeof(depbuf) - 1)
				{
					die("MP_DEP_PREFIXES too long");
				}
				memcpy(depbuf, depprefixes, dn);
				depbuf[dn] = '\0';
			}
			for(tok = strtok_r(depbuf, ":", &save); tok != NULL; tok = strtok_r(NULL, ":", &save))
			{
				/* atomic skip, pre-checked like before the format-free
				 * rewrite: a token that can't feed all three flags is
				 * dropped wholesale, never partially wired (the old
				 * heuristic bound; the bufcat checks below then provably
				 * succeed). */
				if(tok[0] == '\0' || strlen(tok) > ((size_t)MAXVAL - 32) / 2)
				{
					continue;
				}
				{
					size_t off = 0;
 
					if(bufcat(depflag, sizeof(depflag), &off, "-I") == 0
					    || bufcat(depflag, sizeof(depflag), &off, tok) == 0
					    || bufcat(depflag, sizeof(depflag), &off, "/include") == 0)
					{
						continue;
					}
					setflags("CFLAGS", depflag);
					/* CPPFLAGS too - same AC_PROG_CPP reasoning as the
					 * two setflags("CPPFLAGS", ...) calls above. */
					setflags("CPPFLAGS", depflag);
					off = 0;
					if(bufcat(depflag, sizeof(depflag), &off, "-L") == 0
					    || bufcat(depflag, sizeof(depflag), &off, tok) == 0
					    || bufcat(depflag, sizeof(depflag), &off, "/lib -Wl,-rpath,") == 0
					    || bufcat(depflag, sizeof(depflag), &off, tok) == 0
					    || bufcat(depflag, sizeof(depflag), &off, "/lib") == 0)
					{
						continue;
					}
					setflags("LDFLAGS", depflag);
					off = 0;
					if(bufcat(depflag, sizeof(depflag), &off, tok) == 0
					    || bufcat(depflag, sizeof(depflag), &off, "/lib/pkgconfig:") == 0
					    || bufcat(depflag, sizeof(depflag), &off, tok) == 0
					    || bufcat(depflag, sizeof(depflag), &off, "/share/pkgconfig") == 0)
					{
						continue;
					}
					setpath("PKG_CONFIG_PATH", depflag);
				}
			}
		}
		{
			int ei;
 
			for(ei = 4; ei < argc; ei = ei + 1)
			{
				char* eq;
 
				eq = strchr(argv[ei], '=');
				if(eq == NULL)
				{
					continue;
				}
				*eq = '\0';
				if(argv[ei][0] != '\0')
				{
					setenv(argv[ei], eq + 1, 1);
				}
				*eq = '=';
			}
		}
#ifdef __linux__
		if(sandbox == NULL || !(sandbox[0] == 'n' || sandbox[0] == 'N' || sandbox[0] == '0'))
		{
			setup_sandbox(argv[2], prefix);
		}
#endif
		/* /etc/mpx.env, if present, is sourced into this wrapper shell
		 * before the real phase script runs -- an arbitrary shell
		 * script (unlike mpx.conf's fixed key=value grammar), so it
		 * can export any env var or run any setup a build needs, not
		 * just the specific TARGET_/JOBS/ARCH/SANDBOX keys mpx.conf
		 * itself understands. exec (not a second fork) then replaces
		 * this wrapper with the exact same "$SHELL -e <script>"
		 * invocation as before mpx.env existed, keeping every
		 * exported var from it (exec preserves the environment) while
		 * leaving argv[0]/exit-status semantics for the phase script
		 * itself completely unchanged. a failing command inside
		 * mpx.env aborts here (this wrapper also runs under -e), same
		 * "fail loud, not silent" rule as everything else in mpx --
		 * absent entirely (the common case) is silently a no-op. */
		execl(shell, shell, "-e", "-c",
		    "[ -f /etc/mpx.env ] && . /etc/mpx.env; exec \"$1\" -e \"$2\"",
		    shell, shell, argv[3], (char*)NULL);
		_exit(1);
	}
	if(waitpid(pid, &status, 0) < 0)
	{
		die("waitpid failed");
	}
	if(!WIFEXITED(status) || WEXITSTATUS(status) != 0)
	{
		exit(1);
	}
}
powered by btf.