Commits

Michał Górny  committed 71e8029

Switch to PYTHON_SCRIPTROOT-based wrapping.

  • Participants
  • Parent commits 2ff5780
  • Branches python-exec2

Comments (0)

Files changed (16)

 python_exec2_c_SOURCES = src/python-exec-c.c
 
 tests_python_exec2_c_SOURCES = tests/python-exec-c.c
+tests_python_exec2_c_CPPFLAGS = -DTEST_SCRIPTROOT=\"tests\"
 
 tests_print_bufsiz_SOURCES = src/print-bufsiz.c
 
 EXTRA_DIST = src/python-exec.in
 CLEANFILES = python-exec2 \
 	tests/python-exec tests/python-exec2-c \
-	tests/python-exec.c tests/*.tmp*
+	tests/python-exec-c.c tests/*.tmp* tests/*/*.tmp*
 
 python-exec2: src/python-exec.in
 	rm -f $@ $@.tmp
 	@SED@ -e "s|[@]bindir@|${bindir}|" \
+		-e "s|[@]PYTHON_SCRIPTROOT@|${PYTHON_SCRIPTROOT}|" \
 		-e "s|[@]exeext@|${EXEEXT}|g" $< > $@.tmp
 	chmod a-w,a+x $@.tmp
 	mv $@.tmp $@
 tests/python-exec: src/python-exec.in
 	$(MKDIR_P) tests
 	rm -f $@ $@.tmp
-	@SED@ -e "s|[@]bindir@|.|" \
+	@SED@ -e "s|[@]bindir@|./tests|" \
+		-e "s|[@]PYTHON_SCRIPTROOT@|tests|" \
 		-e "s|[@]exeext@|${EXEEXT}|g" $< > $@.tmp
 	chmod a-w,a+x $@.tmp
 	mv $@.tmp $@

File configure.ac

 		[Set to static buffer size for path])
 fi
 
+AC_ARG_WITH([python-scriptroot],
+	[AS_HELP_STRING([--with-python-scriptroot=PATH],
+		[Root directory for wrapped python scripts (default: /usr/lib/python-bin)])],,
+	[
+		with_python_scriptroot=/usr/lib/python-bin
+	])
+AC_DEFINE_UNQUOTED([PYTHON_SCRIPTROOT], ["$with_python_scriptroot"],
+	[Set to the root directory where wrapped Python scripts are stored])
+AC_SUBST([PYTHON_SCRIPTROOT], [$with_python_scriptroot])
+
 AM_INIT_AUTOMAKE([1.11 foreign dist-bzip2 subdir-objects parallel-tests])
 
 m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES])

File src/python-exec-c.c

 #	define BUFFER_SIZE BUFSIZ
 #endif
 
+#ifdef TEST_SCRIPTROOT /* override for tests */
+#	undef PYTHON_SCRIPTROOT
+#	define PYTHON_SCRIPTROOT TEST_SCRIPTROOT
+#endif
+
+/* Python script root directory */
+const char python_scriptroot[] = PYTHON_SCRIPTROOT "/";
 /* All possible EPYTHON values, provided to the configure script. */
 const char* const python_impls[] = { PYTHON_IMPLS };
 /* Maximum length of an EPYTHON value. */
 const char path_sep = '/';
 
 /**
+ * Set path in scriptbuf for given impl.
+ *
+ * @bufp points to the buffer.
+ *
+ * @impl holds the implementation name.
+ *
+ * @progname contains the program basename.
+ */
+ static void set_scriptbuf(char* bufp, const char* impl,
+		const char* progname)
+{
+	memcpy(bufp, python_scriptroot, sizeof(python_scriptroot));
+	if (impl)
+	{
+		bufp += sizeof(python_scriptroot) - 1;
+		strcpy(bufp, impl);
+		strcat(bufp, "/");
+		strcat(bufp, progname);
+	}
+}
+
+/**
  * Try to obtain the value of an environment variable.
  *
- * @bufp points to the space in the buffer where the value shall be
- * written (first byte after the hyphen).
+ * @bufp points to the buffer.
+ *
+ * @progname contains the program basename.
  *
  * @variable contains the environment variable name.
  *
  *
  * Returns 1 on success, 0 otherwise.
  */
-static int try_env(char* bufp, const char* variable, size_t max_len)
+static int try_env(char* bufp, const char* progname,
+		const char* variable, size_t max_len)
 {
 	const char* epython = getenv(variable);
 
 	{
 		if (strlen(epython) <= max_len)
 		{
-			strcpy(bufp, epython);
+			set_scriptbuf(bufp, epython, progname);
 			return 1;
 		}
 		else
 /**
  * Try to read contents of a regular file.
  *
- * @bufp points to the space in the buffer where the value shall be
- * written (first byte after the hyphen).
+ * @bufp points to the buffer.
  *
- * @variable contains the file path.
+ * @progname contains the program basename.
+ *
+ * @path contains the file path.
  *
  * @max_len specifies the maximum value length. The buffer must have
  * at least one byte more for the null terminator.
  *
  * Returns 1 on success, 0 otherwise.
  */
-static int try_file(char* bufp, const char* path, size_t max_len)
+static int try_file(char* bufp, const char* progname,
+		const char* path, size_t max_len)
 {
 	FILE* f = fopen(path, "r");
 
 	if (f)
 	{
-		size_t rd = fread(bufp, 1, max_len, f);
+		size_t rd;
+
+		set_scriptbuf(bufp, 0, 0);
+		bufp += sizeof(python_scriptroot) - 1;
 
+		/* +1 for '\n', +2 to enforce EOF */
+		rd = fread(bufp, 1, max_len+2, f);
 		if (rd > 0 && feof(f))
 		{
-			bufp[rd] = 0;
-			if (bufp[rd-1] == '\n')
-				bufp[rd-1] = 0;
+			if (bufp[rd-2] == '\n')
+				--rd;
+			bufp[rd-1] = '/';
+			strcpy(&bufp[rd], progname);
+
+			fclose(f);
+			return 1;
 		}
 
 		fclose(f);
 	}
 
-	return !!f;
+	return 0;
 }
 
 #ifdef HAVE_READLINK
  * @bufp points to the space in the buffer where the target shall be
  * written (first byte after the hyphen).
  *
- * @variable contains the symlink path.
+ * @progname contains the program basename. May be NULL to disable
+ * scriptbuf syntax enforcing.
+ *
+ * @path contains the symlink path.
  *
  * @max_len specifies the maximum value length. The buffer must have
  * at least one byte more for the null terminator.
  *
  * Returns 1 on success, 0 otherwise.
  */
-static int try_symlink(char* bufp, const char* path, size_t max_len)
+static int try_symlink(char* bufp, const char* progname,
+		const char* path, size_t max_len)
 {
 	size_t rd;
 
+	if (progname)
+	{
+		set_scriptbuf(bufp, 0, 0);
+		bufp += sizeof(python_scriptroot) - 1;
+	}
+
 	errno = 0;
 	/* 1 for the null terminator with max length */
 	rd = readlink(path, bufp, max_len + 1);
 	/* [max_len] could mean that the name is too long */
 	if (rd > 0 && rd < max_len + 1)
 	{
-		bufp[rd] = 0;
+		if (progname)
+		{
+			bufp[rd] = '/';
+			strcpy(&bufp[rd+1], progname);
+		}
+		else
+			bufp[rd] = 0;
 		return 1;
 	}
 
 {
 	const char* const* i;
 	char buf[BUFFER_SIZE];
-	size_t buf_size = sizeof(buf);
-	char* bufpy;
+	char scriptbuf[BUFFER_SIZE];
 
 	const char* script = argv[1];
 	int symlink_resolution = 0;
 		}
 #endif
 
-		/* 2 is for the hyphen and the null terminator. */
-		if (len + max_epython_len + 2 > BUFFER_SIZE)
+		/* length + null terminator */
+		if (len + 1 > BUFFER_SIZE)
 		{
 			fprintf(stderr, "%s: program name longer than buffer size.\n",
 					script);
 			else
 				++fnpos;
 
-			if (!try_symlink(fnpos, buf, len))
+			if (!try_symlink(fnpos, 0, buf, len))
 			{
 				fprintf(stderr, "%s: unable to read symlink at %s: %s.\n",
 						script, buf,
 		}
 #endif
 
-		bufpy = &buf[len+1];
-		bufpy[-1] = '-';
+		fnpos = strrchr(buf, path_sep);
+		if (!fnpos)
+			fnpos = buf;
+		else
+			++fnpos;
+
+		/* scriptroot + '/' + EPYTHON + '/' + basename + '\0' */
+		/* (but sizeof() gives [scriptroot + '/' + '\0']) */
+		len = sizeof(python_scriptroot) + max_epython_len + strlen(fnpos) + 1;
+		if (len >= BUFFER_SIZE)
+		{
+			fprintf(stderr, "%s: program name longer than buffer size.\n",
+					fnpos);
+			return 127;
+		}
 
 		/**
 		 * The implementation check order:
 		 *
 		 * 4) uses the eclass-defined order.
 		 */
-		if (try_env(bufpy, "EPYTHON", max_epython_len))
-			execute(buf, argv);
-		if (try_file(bufpy, EPREFIX "/etc/env.d/python/config", max_epython_len))
-			execute(buf, argv);
+		if (try_env(scriptbuf, fnpos, "EPYTHON", max_epython_len))
+			execute(scriptbuf, argv);
+		if (try_file(scriptbuf, fnpos, EPREFIX "/etc/env.d/python/config", max_epython_len))
+			execute(scriptbuf, argv);
 #ifdef HAVE_READLINK
-		if (try_symlink(bufpy, EPREFIX "/usr/bin/python2", max_epython_len))
-			execute(buf, argv);
-		if (try_symlink(bufpy, EPREFIX "/usr/bin/python3", max_epython_len))
-			execute(buf, argv);
+		if (try_symlink(scriptbuf, fnpos, EPREFIX "/usr/bin/python2", max_epython_len))
+			execute(scriptbuf, argv);
+		if (try_symlink(scriptbuf, fnpos, EPREFIX "/usr/bin/python3", max_epython_len))
+			execute(scriptbuf, argv);
 #endif
 
 		for (i = python_impls; *i; ++i)
 		{
-			strcpy(bufpy, *i);
-			execute(buf, argv);
+			set_scriptbuf(scriptbuf, *i, fnpos);
+			execute(scriptbuf, argv);
 		}
 
-		/**
-		 * Strip the hyphen back and try symlink resolution.
-		 */
-		bufpy[-1] = 0;
-
 #ifdef HAVE_READLINK
 		symlink_resolution = 1;
 #else

File src/python-exec.in

 # __file__ keeps the 'full' name
 
 while True:
-	__file__ = sys.argv[0] + '-' + EPYTHON
+	__file__ = os.path.join('@PYTHON_SCRIPTROOT@', EPYTHON,
+			os.path.basename(sys.argv[0]))
 
 	try:
 		kwargs = {}

File tests/long-path-test

 
 echo "EPYTHON: ${EPYTHON}" >&2
 
-if ! write_impl "${longfn}-${EPYTHON}" "#!/usr/bin/env true"; then
+if ! write_impl "${EPYTHON}" "#!/usr/bin/env true" "-${longfn}"; then
 	do_exit 77
 fi
-mv "${TEST_TMP}" "${TEST_TMP}-${longfn}"
+mv "${TEST_DIR}/${TEST_TMP}" "${TEST_DIR}/${TEST_TMP}-${longfn}"
 
 do_test "${TEST_TMP}-${longfn}"

File tests/pythonwrap-__file__-test

 write_impl "${MY_PYTHON}" "print(__file__)"
 
 # The value should be the same as if variant was run directly.
-V_EXP=$("${MY_PYTHON}" "${TEST_TMP}-${MY_PYTHON}")
-V_GOT=$("${MY_PYTHON}" "${TEST_TMP}")
+V_EXP=$("${MY_PYTHON}" "${TEST_DIR}/${MY_PYTHON}/${TEST_TMP}")
+V_GOT=$("${MY_PYTHON}" "${TEST_DIR}/${TEST_TMP}")
 
 echo "Expected: ${V_EXP}" >&2
 echo "Received: ${V_GOT}" >&2

File tests/runner.sh

 # common metadata
 PYTHON_IMPLS=${1}
 TEST=${2}
+TEST_DIR=tests
 TEST_NAME=${2##*/}
-TEST_TMP=tests/${TEST_NAME}.tmp
+TEST_TMP=${TEST_NAME}.tmp
 
 # helper functions
 write_impl() {
-	echo "${2}" > "${TEST_TMP}-${1}" && \
-	chmod -w,+x "${TEST_TMP}-${1}"
+	mkdir -p "${TEST_DIR}/${1}" && \
+	echo "${2}" > "${TEST_DIR}/${1}/${TEST_TMP}${3}" && \
+	chmod -w,+x "${TEST_DIR}/${1}/${TEST_TMP}${3}"
+}
+
+do_sym() {
+	ln -s "${1}" "${TEST_DIR}/${2}"
 }
 
 do_exit() {
 }
 
 do_test() {
+	if [ ${#} -eq 2 ]; then
+		set -- "${1}" "${TEST_DIR}/${2}"
+	else
+		set -- "${TEST_DIR}/${1}"
+	fi
+
 	set +e
 	echo "Test command: ${@}" >&2
 
 trap 'exit 99' EXIT
 set -e
 
-rm -f "${TEST_TMP}"*
-ln -s python-exec "${TEST_TMP}"
+rm -f "${TEST_DIR}/${TEST_TMP}"* "${TEST_DIR}"/*/"${TEST_TMP}"*
+ln -s python-exec "${TEST_DIR}/${TEST_TMP}"
 . "${TEST}"