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					break;
97				case ENOENT:
98				case ESTALE:
99				case ENOTDIR:
100					/* Those errors indicate the file is missing or not executable
101					   by us, in which case we want to just try the next path
102					   directory.  */
103				case ENODEV:
104				case ETIMEDOUT:
105					/* Some strange filesystems like AFS return even
106					   stranger error numbers.  They cannot reasonably mean
107					   anything else so ignore those, too.  */
108				case ENOEXEC:
109					/* We won't go searching for the shell
110					 * if it is not executable - the Linux
111					 * kernel already handles this enough,
112					 * for us. */
113					break;
114
115				default:
116					/* Some other error means we found an executable file, but
117					   something went wrong executing it; return the error to our
118					   caller.  */
119					return -errno;
120			}
121		} while (*p++ != '\0');
122
123		/* We tried every element and none of them worked.  */
124		if (got_eacces)
125			/* At least one failure was due to permissions, so report that
126			   error.  */
127			return -EACCES;
128	}
129
130	/* Return the error from the last attempt (probably ENOENT).  */
131	return -errno;
132}
133#ifdef TEST
134int main(int argc, char**argv)
135{
136	char buf[PATH_MAX];
137	int ret;
138	argc--;
139	if (!argc) {
140		os_warn("Not enough arguments\n");
141		return 1;
142	}
143	argv++;
144	if (ret = execvp_noalloc(buf, argv[0], argv)) {
145		errno = -ret;
146		perror("execvp_noalloc");
147	}
148	return 0;
149}
150#endif
151