]> git.armaanb.net Git - opendoas.git/blob - libopenbsd/execvpe.c
Add compatibility functions from OpenBSD.
[opendoas.git] / libopenbsd / execvpe.c
1 /*      $OpenBSD: exec.c,v 1.20 2013/01/08 02:26:09 deraadt Exp $ */
2 /*-
3  * Copyright (c) 1991, 1993
4  *      The Regents of the University of California.  All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. Neither the name of the University nor the names of its contributors
15  *    may be used to endorse or promote products derived from this software
16  *    without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30
31 #include <sys/types.h>
32 #include <sys/uio.h>
33
34 #include <errno.h>
35 #include <limits.h>
36 #include <paths.h>
37 #include <stdarg.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42
43 int
44 execvpe(const char *name, char *const *argv, char *const *envp)
45 {
46         char **memp;
47         int cnt;
48         size_t lp, ln, len;
49         char *p;
50         int eacces = 0;
51         char *bp, *cur, *path, buf[PATH_MAX];
52
53         /*
54          * Do not allow null name
55          */
56         if (name == NULL || *name == '\0') {
57                 errno = ENOENT;
58                 return (-1);
59         }
60
61         /* If it's an absolute or relative path name, it's easy. */
62         if (strchr(name, '/')) {
63                 bp = (char *)name;
64                 cur = path = NULL;
65                 goto retry;
66         }
67         bp = buf;
68
69         /* Get the path we're searching. */
70         if (!(path = getenv("PATH")))
71                 path = _PATH_DEFPATH;
72         len = strlen(path) + 1;
73         cur = alloca(len);
74         if (cur == NULL) {
75                 errno = ENOMEM;
76                 return (-1);
77         }
78         strlcpy(cur, path, len);
79         path = cur;
80         while ((p = strsep(&cur, ":"))) {
81                 /*
82                  * It's a SHELL path -- double, leading and trailing colons
83                  * mean the current directory.
84                  */
85                 if (!*p) {
86                         p = ".";
87                         lp = 1;
88                 } else
89                         lp = strlen(p);
90                 ln = strlen(name);
91
92                 /*
93                  * If the path is too long complain.  This is a possible
94                  * security issue; given a way to make the path too long
95                  * the user may execute the wrong program.
96                  */
97                 if (lp + ln + 2 > sizeof(buf)) {
98                         struct iovec iov[3];
99
100                         iov[0].iov_base = "execvp: ";
101                         iov[0].iov_len = 8;
102                         iov[1].iov_base = p;
103                         iov[1].iov_len = lp;
104                         iov[2].iov_base = ": path too long\n";
105                         iov[2].iov_len = 16;
106                         (void)writev(STDERR_FILENO, iov, 3);
107                         continue;
108                 }
109                 bcopy(p, buf, lp);
110                 buf[lp] = '/';
111                 bcopy(name, buf + lp + 1, ln);
112                 buf[lp + ln + 1] = '\0';
113
114 retry:          (void)execve(bp, argv, envp);
115                 switch(errno) {
116                 case E2BIG:
117                         goto done;
118                 case EISDIR:
119                 case ELOOP:
120                 case ENAMETOOLONG:
121                 case ENOENT:
122                         break;
123                 case ENOEXEC:
124                         for (cnt = 0; argv[cnt]; ++cnt)
125                                 ;
126                         memp = alloca((cnt + 2) * sizeof(char *));
127                         if (memp == NULL)
128                                 goto done;
129                         memp[0] = "sh";
130                         memp[1] = bp;
131                         bcopy(argv + 1, memp + 2, cnt * sizeof(char *));
132                         (void)execve(_PATH_BSHELL, memp, envp);
133                         goto done;
134                 case ENOMEM:
135                         goto done;
136                 case ENOTDIR:
137                         break;
138                 case ETXTBSY:
139                         /*
140                          * We used to retry here, but sh(1) doesn't.
141                          */
142                         goto done;
143                 case EACCES:
144                         eacces = 1;
145                         break;
146                 default:
147                         goto done;
148                 }
149         }
150         if (eacces)
151                 errno = EACCES;
152         else if (!errno)
153                 errno = ENOENT;
154 done:
155         return (-1);
156 }