summaryrefslogtreecommitdiff
path: root/src/logic/slbt_exec_execute.c
blob: 7f9389b9f9168c6363540521de0e9554a181bfb6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
/*******************************************************************/
/*  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);
}