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