Blame src/logic/slbt_exec_execute.c

e956c8
/*******************************************************************/
eac61a
/*  slibtool: a strong libtool implementation, written in C        */
49181b
/*  Copyright (C) 2016--2024  SysDeer Technologies, LLC            */
e956c8
/*  Released under the Standard MIT License; see COPYING.SLIBTOOL. */
e956c8
/*******************************************************************/
e956c8
e956c8
#include <string.h>
e956c8
#include <stdbool.h>
e956c8
#include <fcntl.h>
e956c8
#include <errno.h>
e956c8
#include <sys/stat.h>
e956c8
e956c8
#include <slibtool/slibtool.h>
e956c8
#include "slibtool_spawn_impl.h"
7ae5c1
#include "slibtool_driver_impl.h"
19022e
#include "slibtool_snprintf_impl.h"
a47c7a
#include "slibtool_errinfo_impl.h"
e956c8
dfb331
dfb331
/*******************************************************************/
dfb331
/* --mode=execute: wrapper script and execution argument vector    */
dfb331
/*                                                                 */
dfb331
/* The purpose of executable wrapper scripts is to properly handle */
dfb331
/* one common scenario where:                                      */
dfb331
/*                                                                 */
dfb331
/* (1) a build project would entail both executable programs and   */
dfb331
/*     dynamically linked libraries; and                           */
dfb331
/*                                                                 */
dfb331
/* (2) one or more of the project's executable programs would have */
dfb331
/*     one or more of the project's libraries as a dynamic runtime */
dfb331
/*     dependency.                                                 */
dfb331
/*                                                                 */
dfb331
/* Since the project's dependency libraries are not yet installed, */
dfb331
/* correct resolution of a program's dynamic library dependencies  */
dfb331
/* must depend on an appropriate setting of the LD_LIBRARY_PATH    */
dfb331
/* encironment variable (on Linxu or Midipix) or an equivalent     */
dfb331
/* environment variable on any of the other supported platforms.   */
dfb331
/*                                                                 */
dfb331
/* Here, too, there are two distinct cases:                        */
dfb331
/*                                                                 */
dfb331
/* (a) the executable wrapper script is directly invoked from the  */
dfb331
/*     shell command line or otherwise; or                         */
dfb331
/*                                                                 */
dfb331
/* (b) the build target associated with the executable program is  */
dfb331
/*     an argument passed to `slibtool --mode=execute` either as   */
dfb331
/*     the name of the program to be executed (and thus as the     */
dfb331
/*     first non-slibtool argument) or for processing by another   */
dfb331
/*     program.                                                    */
dfb331
/*                                                                 */
dfb331
/* The first case is handled entirely from within the executable   */
dfb331
/* wrapper script. The load environment variable is set based on   */
dfb331
/* the path that was stored in it by slibtool at link time, and    */
dfb331
/* the real executable is then exec'ed.                            */
dfb331
/*                                                                 */
dfb331
/* In the second case, slibtool would start by testing whether the */
dfb331
/* first non-slibtool argument is the wrapper script of a newly    */
dfb331
/* built executable program. If it is, then slibtool would invoke  */
dfb331
/* the wrapper script rather than the executable program, and add  */
dfb331
/* the path to the executable to the argument vector before all    */
dfb331
/* other arguments, makign it ${1} from the wrapper script's point */
dfb331
/* of view. Then again, and irrespective of the previous step,     */
dfb331
/* slibtool will replace any remaining command-line argument which */
dfb331
/* points to a build target associated with a newly built program  */
dfb331
/* with the path to the program itself (that is, the one located   */
dfb331
/* under the internal .libs/ directory).                           */
dfb331
/*                                                                 */
dfb331
/* For the sake of consistency and robustness, wrapper scripts are */
dfb331
/* created at link time for _all_ executable programs, including   */
dfb331
/* those programs which appear to have system libraries as their   */
dfb331
/* sole dynamic library dependencies.                              */
dfb331
/*******************************************************************/
dfb331
dfb331
dfb331
static int slbt_argument_is_an_executable_wrapper_script(
dfb331
	const struct slbt_driver_ctx *  dctx,
dfb331
	const char *                    arg,
dfb331
	char                            (*altarg)[PATH_MAX])
dfb331
{
dfb331
	int           fdcwd;
dfb331
	const char *  base;
dfb331
	char *        mark;
dfb331
	struct stat   st;
dfb331
dfb331
	/* the extra characters: .libs/.exe.wrapper */
dfb331
	if (strlen(arg) > (PATH_MAX - 20))
dfb331
		return SLBT_BUFFER_ERROR(dctx);
dfb331
dfb331
	/* path/to/progname --> path/to/.libs/progname.exe.wapper */
dfb331
	if ((base = strrchr(arg,'/')))
dfb331
		base++;
dfb331
dfb331
	if (!base)
dfb331
		base = arg;
dfb331
dfb331
	mark  = *altarg;
dfb331
	mark += base - arg;
dfb331
dfb331
	strcpy(*altarg,arg);
dfb331
	mark += sprintf(mark,".libs/%s",base);
dfb331
	strcpy(mark,".exe.wrapper");
dfb331
dfb331
	/* not an executable wrapper script? */
dfb331
	fdcwd = slbt_driver_fdcwd(dctx);
dfb331
dfb331
	if (fstatat(fdcwd,*altarg,&st,0) < 0)
dfb331
		return (errno == ENOENT)
dfb331
			? 0 : SLBT_SYSTEM_ERROR(dctx,*altarg);
dfb331
dfb331
	/* path/to/.libs/progname.exe.wapper --> path/to/.libs/progname */
dfb331
	*mark = '\0';
dfb331
dfb331
	return 1;
dfb331
}
dfb331
dfb331
2e30eb
int slbt_exec_execute(const struct slbt_driver_ctx * dctx)
e956c8
{
dfb331
	int			ret;
dfb331
	char **                 parg;
dfb331
	char **                 aarg;
e956c8
	char *			program;
dfb331
	char *                  exeref;
e956c8
	char			wrapper[PATH_MAX];
dfb331
	char			exeprog[PATH_MAX];
dfb331
	char			argbuf [PATH_MAX];
2e30eb
	struct slbt_exec_ctx *	ectx;
e956c8
c4a389
	/* dry run */
c4a389
	if (dctx->cctx->drvflags & SLBT_DRIVER_DRY_RUN)
c4a389
		return 0;
c4a389
e956c8
	/* context */
2e30eb
	if (slbt_ectx_get_exec_ctx(dctx,&ectx) < 0)
2e30eb
		return SLBT_NESTED_ERROR(dctx);
2e30eb
2e30eb
	slbt_disable_placeholders(ectx);
e956c8
dfb331
	/* original and alternate argument vectors */
dfb331
	parg = ectx->cargv;
dfb331
	aarg = ectx->altv;
70949a
dfb331
	/* program (first non-slibtool argument) */
dfb331
	if (!(program = ectx->cargv[0]))
dfb331
		return SLBT_CUSTOM_ERROR(
dfb331
			dctx,
dfb331
			SLBT_ERR_FLOW_ERROR);
7ae5c1
dfb331
	/* init the wrapper argument based on the first qualifying argument */
dfb331
	for (exeref=0; !exeref && *parg; parg++) {
dfb331
		strcpy(argbuf,*parg);
dfb331
dfb331
		if ((ret = slbt_argument_is_an_executable_wrapper_script(
dfb331
				dctx,argbuf,&exeprog)) < 0) {
dfb331
			return SLBT_NESTED_ERROR(dctx);
dfb331
dfb331
		} else if (ret == 1) {
c1ce86
			if (slbt_snprintf(
c1ce86
					wrapper,sizeof(wrapper),
c1ce86
					"%s.exe.wrapper",exeprog) < 0)
c1ce86
				return SLBT_BUFFER_ERROR(dctx);
c1ce86
dfb331
			exeref  = *parg;
dfb331
			*aarg++ = wrapper;
Lucio Andrés Illanes Albornoz f27e85
		} else {
dfb331
			strcpy(*parg,argbuf);
Lucio Andrés Illanes Albornoz f27e85
		}
dfb331
	}
Lucio Andrés Illanes Albornoz f27e85
dfb331
	/* transform in place as needed all arguments */
dfb331
	for (parg=ectx->cargv; *parg; parg++) {
dfb331
		if ((ret = slbt_argument_is_an_executable_wrapper_script(
dfb331
				dctx,*parg,&argbuf)) < 0) {
dfb331
			return SLBT_NESTED_ERROR(dctx);
dfb331
dfb331
		} else if (ret == 1) {
dfb331
			strcpy(*parg,argbuf);
dfb331
			*aarg++ = *parg;
dfb331
		} else {
dfb331
			*aarg++ = *parg;
dfb331
		}
1ed71a
	}
e956c8
dfb331
	*aarg = 0;
dfb331
dfb331
	/* execute mode */
dfb331
	ectx->program = ectx->altv[0];
dfb331
	ectx->argv    = ectx->altv;
dfb331
e956c8
	/* step output */
dfb331
	if (!(dctx->cctx->drvflags & SLBT_DRIVER_SILENT)) {
33a569
		if (slbt_output_execute(ectx)) {
2e30eb
			slbt_ectx_free_exec_ctx(ectx);
a47c7a
			return SLBT_NESTED_ERROR(dctx);
e956c8
		}
dfb331
	}
e956c8
dfb331
	execvp(ectx->altv[0],ectx->altv);
e956c8
2e30eb
	slbt_ectx_free_exec_ctx(ectx);
6beda1
	return SLBT_SYSTEM_ERROR(dctx,0);
e956c8
}