resetting manifest requested domain to floor
[platform/upstream/ccache.git] / execute.c
1 /*
2  * Copyright (C) Andrew Tridgell 2002
3  * Copyright (C) Joel Rosdahl 2011
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the Free
7  * Software Foundation; either version 3 of the License, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13  * more details.
14  *
15  * You should have received a copy of the GNU General Public License along with
16  * this program; if not, write to the Free Software Foundation, Inc., 51
17  * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18  */
19
20 #include "ccache.h"
21
22 static char *
23 find_executable_in_path(const char *name, const char *exclude_name, char *path);
24
25 #ifdef _WIN32
26 /*
27  * Re-create a win32 command line string based on **argv.
28  * http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
29  */
30 static char *
31 argvtos(char *prefix, char **argv)
32 {
33         char *arg;
34         char *ptr;
35         char *str;
36         int l = 0;
37         int i, j;
38
39         i = 0;
40         arg = prefix ? prefix : argv[i++];
41         do {
42                 int bs = 0;
43                 for (j = 0; arg[j]; j++) {
44                         switch (arg[j]) {
45                         case '\\':
46                                 bs++;
47                                 break;
48                         case '"':
49                                 bs = (bs << 1) + 1;
50                         default:
51                                 l += bs + 1;
52                                 bs = 0;
53                         }
54                 }
55                 l += (bs << 1) + 3;
56         } while ((arg = argv[i++]));
57
58         str = ptr = malloc(l + 1);
59         if (str == NULL)
60                 return NULL;
61
62         i = 0;
63         arg = prefix ? prefix : argv[i++];
64         do {
65                 int bs = 0;
66                 *ptr++ = '"';
67                 for (j = 0; arg[j]; j++) {
68                         switch (arg[j]) {
69                         case '\\':
70                                 bs++;
71                                 break;
72                         case '"':
73                                 bs = (bs << 1) + 1;
74                         default:
75                                 while (bs && bs--)
76                                         *ptr++ = '\\';
77                                 *ptr++ = arg[j];
78                         }
79                 }
80                 bs <<= 1;
81                 while (bs && bs--)
82                         *ptr++ = '\\';
83                 *ptr++ = '"';
84                 *ptr++ = ' ';
85         } while ((arg = argv[i++]));
86         ptr[-1] = '\0';
87
88         return str;
89 }
90
91 int
92 win32execute(char *path, char **argv, int doreturn,
93              const char *path_stdout, const char *path_stderr)
94 {
95         PROCESS_INFORMATION pi;
96         STARTUPINFO si;
97         BOOL ret;
98         DWORD exitcode;
99         char *path_env;
100         char *sh = NULL;
101         char *args;
102         const char *ext;
103
104         memset(&pi, 0x00, sizeof(pi));
105         memset(&si, 0x00, sizeof(si));
106
107         ext = get_extension(path);
108         if (ext && strcasecmp(ext, ".sh") == 0 && (path_env = getenv("PATH")))
109                 sh = find_executable_in_path("sh.exe", NULL, path_env);
110         if (!sh && getenv("CCACHE_DETECT_SHEBANG")) {
111                 /* Detect shebang. */
112                 FILE *fp;
113                 fp = fopen(path, "r");
114                 if (fp) {
115                         char buf[10];
116                         fgets(buf, sizeof(buf), fp);
117                         buf[9] = 0;
118                         if (str_eq(buf, "#!/bin/sh") && (path_env = getenv("PATH")))
119                                 sh = find_executable_in_path("sh.exe", NULL, path_env);
120                         fclose(fp);
121                 }
122         }
123         if (sh)
124                 path = sh;
125
126         si.cb = sizeof(STARTUPINFO);
127         if (path_stdout) {
128                 SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
129                 si.hStdOutput = CreateFile(path_stdout, GENERIC_WRITE, 0, &sa,
130                                            CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY |
131                                            FILE_FLAG_SEQUENTIAL_SCAN, NULL);
132                 si.hStdError  = CreateFile(path_stderr, GENERIC_WRITE, 0, &sa,
133                                            CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY |
134                                            FILE_FLAG_SEQUENTIAL_SCAN, NULL);
135                 si.hStdInput  = GetStdHandle(STD_INPUT_HANDLE);
136                 si.dwFlags    = STARTF_USESTDHANDLES;
137                 if (si.hStdOutput == INVALID_HANDLE_VALUE ||
138                     si.hStdError  == INVALID_HANDLE_VALUE)
139                         return -1;
140         }
141         args = argvtos(sh, argv);
142         ret = CreateProcess(path, args, NULL, NULL, 1, 0, NULL, NULL, &si, &pi);
143         free(args);
144         if (path_stdout) {
145                 CloseHandle(si.hStdOutput);
146                 CloseHandle(si.hStdError);
147         }
148         if (ret == 0)
149                 return -1;
150         WaitForSingleObject(pi.hProcess, INFINITE);
151         GetExitCodeProcess(pi.hProcess, &exitcode);
152         CloseHandle(pi.hProcess);
153         CloseHandle(pi.hThread);
154         if (!doreturn)
155                 exit(exitcode);
156         return exitcode;
157 }
158
159 #else
160
161 /*
162   execute a compiler backend, capturing all output to the given paths
163   the full path to the compiler to run is in argv[0]
164 */
165 int
166 execute(char **argv, const char *path_stdout, const char *path_stderr)
167 {
168         pid_t pid;
169         int status;
170
171         cc_log_argv("Executing ", argv);
172
173         pid = fork();
174         if (pid == -1) fatal("Failed to fork: %s", strerror(errno));
175
176         if (pid == 0) {
177                 int fd;
178
179                 tmp_unlink(path_stdout);
180                 fd = open(path_stdout, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL|O_BINARY, 0666);
181                 if (fd == -1) {
182                         exit(1);
183                 }
184                 dup2(fd, 1);
185                 close(fd);
186
187                 tmp_unlink(path_stderr);
188                 fd = open(path_stderr, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL|O_BINARY, 0666);
189                 if (fd == -1) {
190                         exit(1);
191                 }
192                 dup2(fd, 2);
193                 close(fd);
194
195                 exit(execv(argv[0], argv));
196         }
197
198         if (waitpid(pid, &status, 0) != pid) {
199                 fatal("waitpid failed: %s", strerror(errno));
200         }
201
202         if (WEXITSTATUS(status) == 0 && WIFSIGNALED(status)) {
203                 return -1;
204         }
205
206         return WEXITSTATUS(status);
207 }
208 #endif
209
210
211 /*
212  * Find an executable by name in $PATH. Exclude any that are links to
213  * exclude_name.
214 */
215 char *
216 find_executable(const char *name, const char *exclude_name)
217 {
218         char *path;
219
220         if (is_absolute_path(name)) {
221                 return x_strdup(name);
222         }
223
224         path = getenv("CCACHE_PATH");
225         if (!path) {
226                 path = getenv("PATH");
227         }
228         if (!path) {
229                 cc_log("No PATH variable");
230                 return NULL;
231         }
232
233         return find_executable_in_path(name, exclude_name, path);
234 }
235
236 static char *
237 find_executable_in_path(const char *name, const char *exclude_name, char *path)
238 {
239         char *tok, *saveptr = NULL;
240
241         path = x_strdup(path);
242
243         /* search the path looking for the first compiler of the right name
244            that isn't us */
245         for (tok = strtok_r(path, PATH_DELIM, &saveptr);
246              tok;
247              tok = strtok_r(NULL, PATH_DELIM, &saveptr)) {
248 #ifdef _WIN32
249                 char namebuf[MAX_PATH];
250                 int ret = SearchPath(tok, name, ".exe",
251                                      sizeof(namebuf), namebuf, NULL);
252                 if (!ret)
253                         ret = SearchPath(tok, name, NULL,
254                                          sizeof(namebuf), namebuf, NULL);
255                 (void) exclude_name;
256                 if (ret) {
257                         free(path);
258                         return x_strdup(namebuf);
259                 }
260 #else
261                 struct stat st1, st2;
262                 char *fname = format("%s/%s", tok, name);
263                 /* look for a normal executable file */
264                 if (access(fname, X_OK) == 0 &&
265                     lstat(fname, &st1) == 0 &&
266                     stat(fname, &st2) == 0 &&
267                     S_ISREG(st2.st_mode)) {
268                         if (S_ISLNK(st1.st_mode)) {
269                                 char *buf = x_realpath(fname);
270                                 if (buf) {
271                                         char *p = basename(buf);
272                                         if (str_eq(p, exclude_name)) {
273                                                 /* It's a link to "ccache"! */
274                                                 free(p);
275                                                 free(buf);
276                                                 continue;
277                                         }
278                                         free(buf);
279                                         free(p);
280                                 }
281                         }
282
283                         /* Found it! */
284                         free(path);
285                         return fname;
286                 }
287                 free(fname);
288 #endif
289         }
290
291         free(path);
292         return NULL;
293 }
294
295 void
296 print_command(FILE *fp, char **argv)
297 {
298         int i;
299         for (i = 0; argv[i]; i++) {
300                 fprintf(fp, "%s%s",  (i == 0) ? "" : " ", argv[i]);
301         }
302         fprintf(fp, "\n");
303 }
304
305 void
306 print_executed_command(FILE *fp, char **argv)
307 {
308         fprintf(fp, "%s: executing ", MYNAME);
309         print_command(fp, argv);
310 }