1/* Copyright (C) 2006 by Paolo Giarrusso - modified from glibc' execvp.c.
2   Original copyright notice follows:
3
4   Copyright (C) 1991,92,1995-99,2002,2004 Free Software Foundation, Inc.
5   This file is part of the GNU C Library.
6
7   The GNU C Library is free software; you can redistribute it and/or
8   modify it under the terms of the GNU Lesser General Public
9   License as published by the Free Software Foundation; either
10   version 2.1 of the License, or (at your option) any later version.
11
12   The GNU C Library is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15   Lesser General Public License for more details.
16
17   You should have received a copy of the GNU Lesser General Public
18   License along with the GNU C Library; if not, write to the Free
19   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20   02111-1307 USA.  */
21#include <unistd.h>
22
23#include <stdbool.h>
24#include <stdlib.h>
25#include <string.h>
26#include <errno.h>
27#include <limits.h>
28
29#ifndef TEST
30#include <um_malloc.h>
31#else
32#include <stdio.h>
33#define um_kmalloc malloc
34#endif
35#include <os.h>
36
37/* Execute FILE, searching in the `PATH' environment variable if it contains
38   no slashes, with arguments ARGV and environment from `environ'.  */
39int execvp_noalloc(char *buf, const char *file, char *const argv[])
40{
41	if (*file == '\0') {
42		return -ENOENT;
43	}
44
45	if (strchr (file, '/') != NULL) {
46		/* Don't search when it contains a slash.  */
47		execv(file, argv);
48	} else {
49		int got_eacces;
50		size_t len, pathlen;
51		char *name, *p;
52		char *path = getenv("PATH");
53		if (path == NULL)
54			path = ":/bin:/usr/bin";
55
56		len = strlen(file) + 1;
57		pathlen = strlen(path);
58		/* Copy the file name at the top.  */
59		name = memcpy(buf + pathlen + 1, file, len);
60		/* And add the slash.  */
61		*--name = '/';
62
63		got_eacces = 0;
64		p = path;
65		do {
66			char *startp;
67
68			path = p;
69			//Let's avoid this GNU extension.
70			//p = strchrnul (path, ':');
71			p = strchr(path, ':');
72			if (!p)
73				p = strchr(path, '\0');
74
75			if (p == path)
76				/* Two adjacent colons, or a colon at the beginning or the end
77				   of `PATH' means to search the current directory.  */
78				startp = name + 1;
79			else
80				startp = memcpy(name - (p - path), path, p - path);
81
82			/* Try to execute this name.  If it works, execv will not return.  */
83			execv(startp, argv);
84
85			/*
86			if (errno == ENOEXEC) {
87			}
88			*/
89
90			switch (errno) {
91				case EACCES:
92					/* Record the we got a `Permission denied' error.  If we end
93					   up finding no executable we can use, we want to diagnose
94					   that we did find one but were denied access.  */
95					got_eacces = 1;
96				case ENOENT:
97				case ESTALE:
98				case ENOTDIR:
99					/* Those errors indicate the file is missing or not executable
100					   by us, in which case we want to just try the next path
101					   directory.  */
102				case ENODEV:
103				case ETIMEDOUT:
104					/* Some strange filesystems like AFS return even
105					   stranger error numbers.  They cannot reasonably mean
106					   anything else so ignore those, too.  */
107				case ENOEXEC:
108					/* We won't go searching for the shell
109					 * if it is not executable - the Linux
110					 * kernel already handles this enough,
111					 * for us. */
112					break;
113
114				default:
115					/* Some other error means we found an executable file, but
116					   something went wrong executing it; return the error to our
117					   caller.  */
118					return -errno;
119			}
120		} while (*p++ != '\0');
121
122		/* We tried every element and none of them worked.  */
123		if (got_eacces)
124			/* At least one failure was due to permissions, so report that
125			   error.  */
126			return -EACCES;
127	}
128
129	/* Return the error from the last attempt (probably ENOENT).  */
130	return -errno;
131}
132#ifdef TEST
133int main(int argc, char**argv)
134{
135	char buf[PATH_MAX];
136	int ret;
137	argc--;
138	if (!argc) {
139		fprintf(stderr, "Not enough arguments\n");
140		return 1;
141	}
142	argv++;
143	if (ret = execvp_noalloc(buf, argv[0], argv)) {
144		errno = -ret;
145		perror("execvp_noalloc");
146	}
147	return 0;
148}
149#endif
150