1/*
2 * Copyright 2003-2007, Axel D��rfler, axeld@pinc-software.de. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 */
5
6#include <image.h>
7#include <image_private.h>
8
9#include <stdlib.h>
10#include <string.h>
11
12#include <algorithm>
13#include <new>
14
15#include <fs_attr.h>
16
17#include <AutoDeleter.h>
18
19#include <libroot_private.h>
20#include <runtime_loader.h>
21#include <syscalls.h>
22#include <syscall_load_image.h>
23#include <user_runtime.h>
24
25
26struct EnvironmentFilter {
27	EnvironmentFilter()
28		:
29		fBuffer(NULL),
30		fEntries(NULL),
31		fBufferSize(0),
32		fEntryCount(0),
33		fAdditionalEnvCount(0),
34		fNextEntryIndex(0)
35	{
36	}
37
38	~EnvironmentFilter()
39	{
40		free(fBuffer);
41		delete[] fEntries;
42	}
43
44	void Init(const char* path, const char* const* env, size_t envCount)
45	{
46		FileDescriptorCloser fd(open(path, O_RDONLY));
47		if (!fd.IsSet())
48			return;
49
50		static const char* const kEnvAttribute = "SYS:ENV";
51		attr_info info;
52		if (fs_stat_attr(fd.Get(), kEnvAttribute, &info) < 0)
53			return;
54
55		_Init(fd.Get(), kEnvAttribute, info.size, env, envCount);
56	}
57
58	size_t AdditionalSlotsNeeded() const
59	{
60		return fAdditionalEnvCount;
61	}
62
63	size_t AdditionalSizeNeeded() const
64	{
65		return fBufferSize + fAdditionalEnvCount * sizeof(char*);
66	}
67
68	size_t PrepareSlot(const char* env, int32 index, char* buffer)
69	{
70		if (fNextEntryIndex < fEntryCount
71			&& fEntries[fNextEntryIndex].index == index) {
72			env = fEntries[fNextEntryIndex].replacement;
73			fNextEntryIndex++;
74		}
75
76		return _FillSlot(env, buffer);
77	}
78
79	void PrepareAdditionalSlots(char**& slot, char*& buffer)
80	{
81		for (size_t i = 0; i < fAdditionalEnvCount; i++) {
82			size_t envSize = _FillSlot(fEntries[i].replacement, buffer);
83			*slot++ = buffer;
84			buffer += envSize;
85		}
86	}
87
88private:
89	void _Init(int fd, const char* attribute, size_t size,
90		const char* const* env, size_t envCount)
91	{
92		if (size == 0)
93			return;
94
95		// read the attribute
96		char* buffer = (char*)malloc(size + 1);
97		if (buffer == NULL)
98			return;
99		MemoryDeleter bufferDeleter(buffer);
100
101		ssize_t bytesRead = fs_read_attr(fd, attribute, B_STRING_TYPE, 0,
102			buffer, size);
103		if (bytesRead < 0 || (size_t)bytesRead != size)
104			return;
105		buffer[size] = '\0';
106
107		// deescape the buffer and count the entries
108		size_t entryCount = 1;
109		char* out = buffer;
110		for (const char* c = buffer; *c != '\0'; c++) {
111			if (*c == '\\') {
112				c++;
113				if (*c == '\0')
114					break;
115				if (*c == '0') {
116					*out++ = '\0';
117					entryCount++;
118				} else
119					*out++ = *c;
120			} else
121				*out++ = *c;
122		}
123		*out++ = '\0';
124		size = out - buffer + 1;
125
126		// create an entry array
127		fEntries = new(std::nothrow) Entry[entryCount];
128		if (fEntries == NULL)
129			return;
130
131		bufferDeleter.Detach();
132		fBuffer = buffer;
133		fBufferSize = size;
134
135		// init the entries
136		out = buffer;
137		for (size_t i = 0; i < entryCount; i++) {
138			const char* separator = strchr(out, '=');
139			if (separator != NULL && separator != out) {
140				fEntries[fEntryCount].replacement = out;
141				fEntries[fEntryCount].index = _FindEnvEntry(env, envCount, out,
142					separator - out);
143				if (fEntries[fEntryCount].index < 0)
144					fAdditionalEnvCount++;
145				fEntryCount++;
146			}
147			out += strlen(out) + 1;
148		}
149
150		if (fEntryCount > 1)
151			std::sort(fEntries, fEntries + fEntryCount);
152
153		// Advance fNextEntryIndex to the first entry pointing to an existing
154		// env variable.
155		while (fNextEntryIndex < fEntryCount
156			&& fEntries[fNextEntryIndex].index < 0) {
157			fNextEntryIndex++;
158		}
159	}
160
161	int32 _FindEnvEntry(const char* const* env, size_t envCount,
162		const char* variable, size_t variableLength)
163	{
164		for (size_t i = 0; i < envCount; i++) {
165			if (strncmp(env[i], variable, variableLength) == 0
166				&& env[i][variableLength] == '=') {
167				return i;
168			}
169		}
170
171		return -1;
172	}
173
174	size_t _FillSlot(const char* env, char* buffer)
175	{
176		size_t envSize = strlen(env) + 1;
177		memcpy(buffer, env, envSize);
178		return envSize;
179	}
180
181private:
182	struct Entry {
183		char*	replacement;
184		int32	index;
185
186		bool operator<(const Entry& other) const
187		{
188			return index < other.index;
189		}
190	};
191
192private:
193	char*	fBuffer;
194	Entry*	fEntries;
195	size_t	fBufferSize;
196	size_t	fEntryCount;
197	size_t	fAdditionalEnvCount;
198	size_t	fNextEntryIndex;
199};
200
201
202thread_id
203__load_image_at_path(const char* path, int32 argCount, const char **args,
204	const char **environ)
205{
206	char invoker[B_FILE_NAME_LENGTH];
207	char **newArgs = NULL;
208	int32 envCount = 0;
209	thread_id thread;
210
211	if (argCount < 1 || environ == NULL)
212		return B_BAD_VALUE;
213
214	// test validity of executable + support for scripts
215	{
216		status_t status = __test_executable(path, invoker);
217		if (status < B_OK)
218			return status;
219
220		if (invoker[0]) {
221			status = __parse_invoke_line(invoker, &newArgs,
222				(char * const **)&args, &argCount, path);
223			if (status < B_OK)
224				return status;
225		}
226	}
227
228	// count environment variables
229	while (environ[envCount] != NULL)
230		envCount++;
231
232	char** flatArgs = NULL;
233	size_t flatArgsSize;
234	status_t status = __flatten_process_args(args, argCount, environ,
235		&envCount, path, &flatArgs, &flatArgsSize);
236
237	if (status == B_OK) {
238		thread = _kern_load_image(flatArgs, flatArgsSize, argCount, envCount,
239			B_NORMAL_PRIORITY, B_WAIT_TILL_LOADED, -1, 0);
240
241		free(flatArgs);
242	} else
243		thread = status;
244
245	free(newArgs);
246	return thread;
247}
248
249
250thread_id
251load_image(int32 argCount, const char **args, const char **environ)
252{
253	return __load_image_at_path(args[0], argCount, args, environ);
254}
255
256
257image_id
258load_add_on(char const *name)
259{
260	if (name == NULL)
261		return B_BAD_VALUE;
262
263	return __gRuntimeLoader->load_add_on(name, 0);
264}
265
266
267status_t
268unload_add_on(image_id id)
269{
270	return __gRuntimeLoader->unload_add_on(id);
271}
272
273
274status_t
275get_image_symbol(image_id id, char const *symbolName, int32 symbolType,
276	void **_location)
277{
278	return __gRuntimeLoader->get_image_symbol(id, symbolName, symbolType,
279		false, NULL, _location);
280}
281
282
283status_t
284get_image_symbol_etc(image_id id, char const *symbolName, int32 symbolType,
285	bool recursive, image_id *_inImage, void **_location)
286{
287	return __gRuntimeLoader->get_image_symbol(id, symbolName, symbolType,
288		recursive, _inImage, _location);
289}
290
291
292status_t
293get_nth_image_symbol(image_id id, int32 num, char *nameBuffer, int32 *_nameLength,
294	int32 *_symbolType, void **_location)
295{
296	return __gRuntimeLoader->get_nth_image_symbol(id, num, nameBuffer, _nameLength, _symbolType, _location);
297}
298
299
300status_t
301_get_image_info(image_id id, image_info *info, size_t infoSize)
302{
303	return _kern_get_image_info(id, info, infoSize);
304}
305
306
307status_t
308_get_next_image_info(team_id team, int32 *cookie, image_info *info, size_t infoSize)
309{
310	return _kern_get_next_image_info(team, cookie, info, infoSize);
311}
312
313
314void
315clear_caches(void *address, size_t length, uint32 flags)
316{
317	_kern_clear_caches(address, length, flags);
318}
319
320
321//	#pragma mark -
322
323
324status_t
325__look_up_in_path(const char *file, char *buffer)
326{
327	// get the PATH
328	const char* paths = getenv("PATH");
329	if (paths == NULL)
330		return B_ENTRY_NOT_FOUND;
331
332	int fileNameLen = strlen(file);
333
334	// iterate through the paths
335	const char* pathEnd = paths - 1;
336	while (pathEnd != NULL) {
337		paths = pathEnd + 1;
338		pathEnd = strchr(paths, ':');
339		int pathLen = (pathEnd ? pathEnd - paths : strlen(paths));
340
341		// We skip empty paths and those that would become too long.
342		// The latter is not really correct, but practically irrelevant.
343		if (pathLen == 0
344			|| pathLen + 1 + fileNameLen >= B_PATH_NAME_LENGTH) {
345			continue;
346		}
347
348		// concatinate the program path
349		char path[B_PATH_NAME_LENGTH];
350		memcpy(path, paths, pathLen);
351		path[pathLen] = '\0';
352
353		if (path[pathLen - 1] != '/')
354			strcat(path, "/");
355		strcat(path, file);
356
357		// check whether it is a file
358		struct stat st;
359		if (stat(path, &st) != 0 || !S_ISREG(st.st_mode))
360			continue;
361
362		// if executable, we've found what we are looking for
363		if (access(path, X_OK) == 0) {
364			strlcpy(buffer, path, B_PATH_NAME_LENGTH);
365			return B_OK;
366		}
367	}
368
369	return B_ENTRY_NOT_FOUND;
370}
371
372
373static char *
374next_argument(char **_start, bool separate)
375{
376	char *line = *_start;
377	char quote = 0;
378	int32 i;
379
380	// eliminate leading spaces
381	while (line[0] == ' ')
382		line++;
383
384	if (line[0] == '"' || line[0] == '\'') {
385		quote = line[0];
386		line++;
387	}
388
389	if (!line[0])
390		return NULL;
391
392	for (i = 0;; i++) {
393		if (line[i] == '\\' && line[i + 1] != '\0')
394			continue;
395
396		if (line[i] == '\0') {
397			*_start = &line[i];
398			return line;
399		}
400		if ((!quote && line[i] == ' ') || line[i] == quote) {
401			// argument separator!
402			if (separate)
403				line[i] = '\0';
404			*_start = &line[i + 1];
405			return line;
406		}
407	}
408
409	return NULL;
410}
411
412
413status_t
414__parse_invoke_line(char *invoker, char ***_newArgs,
415	char * const **_oldArgs, int32 *_argCount, const char *arg0)
416{
417	int32 i, count = 0;
418	char *arg = invoker;
419	char **newArgs;
420
421	// count arguments in the line
422
423	while (next_argument(&arg, false)) {
424		count++;
425	}
426
427	// this is a shell script and requires special treatment
428	newArgs = (char**)malloc((*_argCount + count + 1) * sizeof(void *));
429	if (newArgs == NULL)
430		return B_NO_MEMORY;
431
432	// copy invoker and old arguments and to newArgs
433
434	for (i = 0; (arg = next_argument(&invoker, true)) != NULL; i++) {
435		newArgs[i] = arg;
436	}
437	for (i = 0; i < *_argCount; i++) {
438		if (i == 0)
439			newArgs[i + count] = (char*)arg0;
440		else
441			newArgs[i + count] = (char *)(*_oldArgs)[i];
442	}
443
444	newArgs[i + count] = NULL;
445
446	*_newArgs = newArgs;
447	*_oldArgs = (char * const *)newArgs;
448	*_argCount += count;
449
450	return B_OK;
451}
452
453
454status_t
455__get_next_image_dependency(image_id id, uint32 *cookie, const char **_name)
456{
457	return __gRuntimeLoader->get_next_image_dependency(id, cookie, _name);
458}
459
460
461status_t
462__test_executable(const char *path, char *invoker)
463{
464	return __gRuntimeLoader->test_executable(path, invoker);
465}
466
467
468/*!	Allocates a flat buffer and copies the argument and environment strings
469	into it. The buffer starts with a char* array which contains pointers to
470	the strings of the arguments and environment, followed by the strings. Both
471	arguments and environment arrays are NULL-terminated.
472
473	If executablePath is non-NULL, it should refer to the executable to be
474	executed. If the executable file specifies changes to environment variable
475	values, those will be performed.
476*/
477status_t
478__flatten_process_args(const char* const* args, int32 argCount,
479	const char* const* env, int32* _envCount, const char* executablePath,
480	char*** _flatArgs, size_t* _flatSize)
481{
482	if (args == NULL || _envCount == NULL || (env == NULL && *_envCount != 0))
483		return B_BAD_VALUE;
484
485	int32 envCount = *_envCount;
486
487	// determine total needed size
488	int32 argSize = 0;
489	for (int32 i = 0; i < argCount; i++) {
490		if (args[i] == NULL)
491			return B_BAD_VALUE;
492		argSize += strlen(args[i]) + 1;
493	}
494
495	int32 envSize = 0;
496	for (int32 i = 0; i < envCount; i++) {
497		if (env[i] == NULL)
498			return B_BAD_VALUE;
499		envSize += strlen(env[i]) + 1;
500	}
501
502	EnvironmentFilter envFilter;
503	if (executablePath != NULL)
504		envFilter.Init(executablePath, env, envCount);
505
506	int32 totalSlotCount = argCount + envCount + 2
507		+ envFilter.AdditionalSlotsNeeded();
508	int32 size = totalSlotCount * sizeof(char*) + argSize + envSize
509		+ envFilter.AdditionalSizeNeeded();
510	if (size > MAX_PROCESS_ARGS_SIZE)
511		return B_TOO_MANY_ARGS;
512
513	// allocate space
514	char** flatArgs = (char**)malloc(size);
515	if (flatArgs == NULL)
516		return B_NO_MEMORY;
517
518	char** slot = flatArgs;
519	char* stringSpace = (char*)(flatArgs + totalSlotCount);
520
521	// copy arguments and environment
522	for (int32 i = 0; i < argCount; i++) {
523		int32 argSize = strlen(args[i]) + 1;
524		memcpy(stringSpace, args[i], argSize);
525		*slot++ = stringSpace;
526		stringSpace += argSize;
527	}
528
529	*slot++ = NULL;
530
531	for (int32 i = 0; i < envCount; i++) {
532		size_t envSize = envFilter.PrepareSlot(env[i], i, stringSpace);
533		*slot++ = stringSpace;
534		stringSpace += envSize;
535	}
536
537	envFilter.PrepareAdditionalSlots(slot, stringSpace);
538
539	*slot++ = NULL;
540
541	*_envCount = envCount + envFilter.AdditionalSlotsNeeded();
542	*_flatArgs = flatArgs;
543	*_flatSize = stringSpace - (char*)flatArgs;
544	return B_OK;
545}
546
547
548extern "C" void _call_init_routines_(void);
549void
550_call_init_routines_(void)
551{
552	// This is called by the original BeOS startup code.
553	// We don't need it, because our loader already does the job, right?
554}
555
556