xref: /haiku/src/system/libroot/posix/unistd/exec.cpp (revision 2cad94c1c30b6223ad8c08710b26e071d32e9979)
1 /*
2  * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Copyright 2004-2015, Axel Dörfler, axeld@pinc-software.de.
4  * Distributed under the terms of the MIT License.
5  */
6 
7 
8 #include <libroot_private.h>
9 #include <syscalls.h>
10 
11 #include <alloca.h>
12 #include <errno.h>
13 #include <stdarg.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <umask.h>
18 #include <unistd.h>
19 
20 #include <errno_private.h>
21 
22 
23 static int
24 count_arguments(va_list list, const char* arg, char*** _env)
25 {
26 	int count = 0;
27 
28 	while (arg != NULL) {
29 		count++;
30 		arg = va_arg(list, const char*);
31 	}
32 
33 	if (_env)
34 		*_env = va_arg(list, char**);
35 
36 	return count;
37 }
38 
39 
40 static void
41 copy_arguments(va_list list, const char** args, const char* arg)
42 {
43 	int count = 0;
44 
45 	while (arg != NULL) {
46 		args[count++] = arg;
47 		arg = va_arg(list, const char*);
48 	}
49 
50 	args[count] = NULL;
51 		// terminate list
52 }
53 
54 
55 static int
56 do_exec(const char* path, char* const args[], char* const environment[],
57 	bool useDefaultInterpreter)
58 {
59 	if (path == NULL || args == NULL) {
60 		__set_errno(B_BAD_VALUE);
61 		return -1;
62 	}
63 
64 	// Count argument/environment list entries here, we don't want
65 	// to do this in the kernel
66 	int32 argCount = 0;
67 	while (args[argCount] != NULL) {
68 		argCount++;
69 	}
70 
71 	int32 envCount = 0;
72 	if (environment != NULL) {
73 		while (environment[envCount] != NULL) {
74 			envCount++;
75 		}
76 	}
77 
78 	if (argCount == 0) {
79 		// we need some more info on what to do...
80 		__set_errno(B_BAD_VALUE);
81 		return -1;
82 	}
83 
84 	// Test validity of executable + support for scripts
85 	char invoker[B_FILE_NAME_LENGTH];
86 	status_t status = __test_executable(path, invoker);
87 	if (status < B_OK) {
88 		if (status == B_NOT_AN_EXECUTABLE && useDefaultInterpreter) {
89 			strcpy(invoker, "/bin/sh");
90 			status = B_OK;
91 		} else {
92 			__set_errno(status);
93 			return -1;
94 		}
95 	}
96 
97 	char** newArgs = NULL;
98 	if (invoker[0] != '\0') {
99 		status = __parse_invoke_line(invoker, &newArgs, &args, &argCount, path);
100 		if (status < B_OK) {
101 			__set_errno(status);
102 			return -1;
103 		}
104 
105 		path = newArgs[0];
106 	}
107 
108 	char** flatArgs = NULL;
109 	size_t flatArgsSize;
110 	status = __flatten_process_args(newArgs ? newArgs : args, argCount,
111 		environment, &envCount, path, &flatArgs, &flatArgsSize);
112 
113 	if (status == B_OK) {
114 		__set_errno(_kern_exec(path, flatArgs, flatArgsSize, argCount, envCount,
115 			__gUmask));
116 			// if this call returns, something definitely went wrong
117 
118 		free(flatArgs);
119 	} else
120 		__set_errno(status);
121 
122 	free(newArgs);
123 	return -1;
124 }
125 
126 
127 //	#pragma mark -
128 
129 
130 int
131 execve(const char* path, char* const args[], char* const environment[])
132 {
133 	return do_exec(path, args, environment, false);
134 }
135 
136 
137 int
138 execv(const char* path, char* const argv[])
139 {
140 	return do_exec(path, argv, environ, false);
141 }
142 
143 
144 int
145 execvp(const char* file, char* const argv[])
146 {
147 	return execvpe(file, argv, environ);
148 }
149 
150 
151 int
152 execvpe(const char* file, char* const argv[], char* const environment[])
153 {
154 	// let do_exec() handle cases where file is a path (or invalid)
155 	if (file == NULL || strchr(file, '/') != NULL)
156 		return do_exec(file, argv, environment, true);
157 
158 	// file is just a leaf name, so we have to look it up in the path
159 
160 	// get the PATH
161 	const char* paths = getenv("PATH");
162 	if (paths == NULL) {
163 		__set_errno(B_ENTRY_NOT_FOUND);
164 		return -1;
165 	}
166 
167 	int fileNameLen = strlen(file);
168 
169 	// iterate through the paths
170 	const char* pathEnd = paths - 1;
171 	while (pathEnd != NULL) {
172 		paths = pathEnd + 1;
173 		pathEnd = strchr(paths, ':');
174 		int pathLen = (pathEnd ? pathEnd - paths : strlen(paths));
175 
176 		// We skip empty paths and those that would become too long.
177 		// The latter is not really correct, but practically irrelevant.
178 		if (pathLen == 0
179 			|| pathLen + 1 + fileNameLen >= B_PATH_NAME_LENGTH) {
180 			continue;
181 		}
182 
183 		// concatinate the program path
184 		char path[B_PATH_NAME_LENGTH];
185 		memcpy(path, paths, pathLen);
186 		path[pathLen] = '\0';
187 
188 		if (path[pathLen - 1] != '/')
189 			strcat(path, "/");
190 		strcat(path, file);
191 
192 		// check whether it is a file
193 		struct stat st;
194 		if (stat(path, &st) != 0 || !S_ISREG(st.st_mode))
195 			continue;
196 
197 		// if executable, execute it
198 		if (access(path, X_OK) == 0)
199 			return do_exec(path, argv, environment, true);
200 	}
201 
202 	__set_errno(B_ENTRY_NOT_FOUND);
203 	return -1;
204 }
205 
206 
207 int
208 execl(const char* path, const char* arg, ...)
209 {
210 	const char** args;
211 	va_list list;
212 	int count;
213 
214 	// count arguments
215 
216 	va_start(list, arg);
217 	count = count_arguments(list, arg, NULL);
218 	va_end(list);
219 
220 	// copy arguments
221 
222 	args = (const char**)alloca((count + 1) * sizeof(char*));
223 	va_start(list, arg);
224 	copy_arguments(list, args, arg);
225 	va_end(list);
226 
227 	return do_exec(path, (char* const*)args, environ, false);
228 }
229 
230 
231 int
232 execlp(const char* file, const char* arg, ...)
233 {
234 	const char** args;
235 	va_list list;
236 	int count;
237 
238 	// count arguments
239 
240 	va_start(list, arg);
241 	count = count_arguments(list, arg, NULL);
242 	va_end(list);
243 
244 	// copy arguments
245 
246 	args = (const char**)alloca((count + 1) * sizeof(char*));
247 	va_start(list, arg);
248 	copy_arguments(list, args, arg);
249 	va_end(list);
250 
251 	return execvp(file, (char* const*)args);
252 }
253 
254 
255 int
256 execle(const char* path, const char* arg, ... /*, char** env */)
257 {
258 	const char** args;
259 	char** env;
260 	va_list list;
261 	int count;
262 
263 	// count arguments
264 
265 	va_start(list, arg);
266 	count = count_arguments(list, arg, &env);
267 	va_end(list);
268 
269 	// copy arguments
270 
271 	args = (const char**)alloca((count + 1) * sizeof(char*));
272 	va_start(list, arg);
273 	copy_arguments(list, args, arg);
274 	va_end(list);
275 
276 	return do_exec(path, (char* const*)args, env, false);
277 }
278 
279 // TODO: remove this again if possible
280 extern int exect(const char *path, char *const *argv);
281 
282 int
283 exect(const char* path, char* const* argv)
284 {
285 	return execv(path, argv);
286 }
287