Imported Upstream version 3.2.6
[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 extern struct conf *conf;
23
24 static char *
25 find_executable_in_path(const char *name, const char *exclude_name, char *path);
26
27 #ifdef _WIN32
28 /*
29  * Re-create a win32 command line string based on **argv.
30  * http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
31  */
32 char *
33 win32argvtos(char *prefix, char **argv)
34 {
35         char *arg;
36         char *ptr;
37         char *str;
38         int l = 0;
39         int i, j;
40
41         i = 0;
42         arg = prefix ? prefix : argv[i++];
43         do {
44                 int bs = 0;
45                 for (j = 0; arg[j]; j++) {
46                         switch (arg[j]) {
47                         case '\\':
48                                 bs++;
49                                 break;
50                         case '"':
51                                 bs = (bs << 1) + 1;
52                         default:
53                                 l += bs + 1;
54                                 bs = 0;
55                         }
56                 }
57                 l += (bs << 1) + 3;
58         } while ((arg = argv[i++]));
59
60         str = ptr = malloc(l + 1);
61         if (!str)
62                 return NULL;
63
64         i = 0;
65         arg = prefix ? prefix : argv[i++];
66         do {
67                 int bs = 0;
68                 *ptr++ = '"';
69                 for (j = 0; arg[j]; j++) {
70                         switch (arg[j]) {
71                         case '\\':
72                                 bs++;
73                                 break;
74                         case '"':
75                                 bs = (bs << 1) + 1;
76                         default:
77                                 while (bs && bs--)
78                                         *ptr++ = '\\';
79                                 *ptr++ = arg[j];
80                         }
81                 }
82                 bs <<= 1;
83                 while (bs && bs--)
84                         *ptr++ = '\\';
85                 *ptr++ = '"';
86                 *ptr++ = ' ';
87                 /* cppcheck-suppress unreadVariable */
88         } while ((arg = argv[i++]));
89         ptr[-1] = '\0';
90
91         return str;
92 }
93
94 char *
95 win32getshell(char *path)
96 {
97         char *path_env;
98         char *sh = NULL;
99         const char *ext;
100
101         ext = get_extension(path);
102         if (ext && strcasecmp(ext, ".sh") == 0 && (path_env = getenv("PATH")))
103                 sh = find_executable_in_path("sh.exe", NULL, path_env);
104         if (!sh && getenv("CCACHE_DETECT_SHEBANG")) {
105                 /* Detect shebang. */
106                 FILE *fp;
107                 fp = fopen(path, "r");
108                 if (fp) {
109                         char buf[10];
110                         fgets(buf, sizeof(buf), fp);
111                         buf[9] = 0;
112                         if (str_eq(buf, "#!/bin/sh") && (path_env = getenv("PATH")))
113                                 sh = find_executable_in_path("sh.exe", NULL, path_env);
114                         fclose(fp);
115                 }
116         }
117
118         return sh;
119 }
120
121 void add_exe_ext_if_no_to_fullpath(char *full_path_win_ext, size_t max_size,
122                                    const char *ext, const char *path) {
123         if (!ext || (!str_eq(".exe", ext)
124                      && !str_eq(".bat", ext)
125                      && !str_eq(".EXE", ext)
126                      && !str_eq(".BAT", ext))) {
127                 snprintf(full_path_win_ext, max_size, "%s.exe", path);
128         } else {
129                 snprintf(full_path_win_ext, max_size, "%s", path);
130         }
131 }
132
133 int
134 win32execute(char *path, char **argv, int doreturn,
135              int fd_stdout, int fd_stderr)
136 {
137         PROCESS_INFORMATION pi;
138         STARTUPINFO si;
139         BOOL ret;
140         DWORD exitcode;
141         char *sh = NULL;
142         char *args;
143
144         memset(&pi, 0x00, sizeof(pi));
145         memset(&si, 0x00, sizeof(si));
146
147         sh = win32getshell(path);
148         if (sh)
149                 path = sh;
150
151         si.cb = sizeof(STARTUPINFO);
152         if (fd_stdout != -1) {
153                 si.hStdOutput = (HANDLE)_get_osfhandle(fd_stdout);
154                 si.hStdError = (HANDLE)_get_osfhandle(fd_stderr);
155                 si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
156                 si.dwFlags = STARTF_USESTDHANDLES;
157                 if (si.hStdOutput == INVALID_HANDLE_VALUE
158                     || si.hStdError == INVALID_HANDLE_VALUE) {
159                         return -1;
160                 }
161         } else {
162                 /* redirect subprocess stdout, stderr into current process */
163                 si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
164                 si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
165                 si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
166                 si.dwFlags = STARTF_USESTDHANDLES;
167                 if (si.hStdOutput == INVALID_HANDLE_VALUE
168                     || si.hStdError == INVALID_HANDLE_VALUE) {
169                         return -1;
170                 }
171         }
172         args = win32argvtos(sh, argv);
173
174         const char *ext = strrchr(path, '.');
175         char full_path_win_ext[MAX_PATH] = {0};
176         add_exe_ext_if_no_to_fullpath(full_path_win_ext, MAX_PATH, ext, path);
177         ret = CreateProcess(full_path_win_ext, args, NULL, NULL, 1, 0, NULL, NULL,
178                             &si, &pi);
179         if (fd_stdout != -1) {
180                 close(fd_stdout);
181                 close(fd_stderr);
182         }
183         free(args);
184         if (ret == 0) {
185                 LPVOID lpMsgBuf;
186                 LPVOID lpDisplayBuf;
187                 DWORD dw = GetLastError();
188
189                 FormatMessage(
190                   FORMAT_MESSAGE_ALLOCATE_BUFFER |
191                   FORMAT_MESSAGE_FROM_SYSTEM |
192                   FORMAT_MESSAGE_IGNORE_INSERTS,
193                   NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf,
194                   0, NULL);
195
196                 lpDisplayBuf =
197                   (LPVOID) LocalAlloc(LMEM_ZEROINIT,
198                                       (lstrlen((LPCTSTR) lpMsgBuf)
199                                        + lstrlen((LPCTSTR) __FILE__) + 200)
200                                       * sizeof(TCHAR));
201                 _snprintf((LPTSTR) lpDisplayBuf,
202                           LocalSize(lpDisplayBuf) / sizeof(TCHAR),
203                           TEXT("%s failed with error %d: %s"), __FILE__, dw, lpMsgBuf);
204
205                 cc_log("can't execute %s; OS returned error: %s",
206                        full_path_win_ext, (char *)lpDisplayBuf);
207
208                 LocalFree(lpMsgBuf);
209                 LocalFree(lpDisplayBuf);
210
211                 return -1;
212         }
213         WaitForSingleObject(pi.hProcess, INFINITE);
214         GetExitCodeProcess(pi.hProcess, &exitcode);
215         CloseHandle(pi.hProcess);
216         CloseHandle(pi.hThread);
217         if (!doreturn)
218                 x_exit(exitcode);
219         return exitcode;
220 }
221
222 #else
223
224 /* Execute a compiler backend, capturing all output to the given paths the full
225  * path to the compiler to run is in argv[0]. */
226 int
227 execute(char **argv, int fd_out, int fd_err, pid_t *pid)
228 {
229         int status;
230
231         cc_log_argv("Executing ", argv);
232
233         block_signals();
234         *pid = fork();
235         unblock_signals();
236
237         if (*pid == -1) {
238                 fatal("Failed to fork: %s", strerror(errno));
239         }
240
241         if (*pid == 0) {
242                 /* Child. */
243                 dup2(fd_out, 1);
244                 close(fd_out);
245                 dup2(fd_err, 2);
246                 close(fd_err);
247                 x_exit(execv(argv[0], argv));
248         }
249
250         close(fd_out);
251         close(fd_err);
252
253         if (waitpid(*pid, &status, 0) != *pid) {
254                 fatal("waitpid failed: %s", strerror(errno));
255         }
256
257         block_signals();
258         *pid = 0;
259         unblock_signals();
260
261         if (WEXITSTATUS(status) == 0 && WIFSIGNALED(status)) {
262                 return -1;
263         }
264
265         return WEXITSTATUS(status);
266 }
267 #endif
268
269
270 /*
271  * Find an executable by name in $PATH. Exclude any that are links to
272  * exclude_name.
273  */
274 char *
275 find_executable(const char *name, const char *exclude_name)
276 {
277         char *path;
278
279         if (is_absolute_path(name)) {
280                 return x_strdup(name);
281         }
282
283         path = conf->path;
284         if (str_eq(path, "")) {
285                 path = getenv("PATH");
286         }
287         if (!path) {
288                 cc_log("No PATH variable");
289                 return NULL;
290         }
291
292         return find_executable_in_path(name, exclude_name, path);
293 }
294
295 static char *
296 find_executable_in_path(const char *name, const char *exclude_name, char *path)
297 {
298         char *tok, *saveptr = NULL;
299
300         path = x_strdup(path);
301
302         /* search the path looking for the first compiler of the right name
303            that isn't us */
304         for (tok = strtok_r(path, PATH_DELIM, &saveptr);
305              tok;
306              tok = strtok_r(NULL, PATH_DELIM, &saveptr)) {
307 #ifdef _WIN32
308                 char namebuf[MAX_PATH];
309                 int ret = SearchPath(tok, name, NULL,
310                                      sizeof(namebuf), namebuf, NULL);
311                 if (!ret) {
312                         char *exename = format("%s.exe", name);
313                         ret = SearchPath(tok, exename, NULL,
314                                          sizeof(namebuf), namebuf, NULL);
315                         free(exename);
316                 }
317                 (void) exclude_name;
318                 if (ret) {
319                         free(path);
320                         return x_strdup(namebuf);
321                 }
322 #else
323                 struct stat st1, st2;
324                 char *fname = format("%s/%s", tok, name);
325                 /* look for a normal executable file */
326                 if (access(fname, X_OK) == 0 &&
327                     lstat(fname, &st1) == 0 &&
328                     stat(fname, &st2) == 0 &&
329                     S_ISREG(st2.st_mode)) {
330                         if (S_ISLNK(st1.st_mode)) {
331                                 char *buf = x_realpath(fname);
332                                 if (buf) {
333                                         char *p = basename(buf);
334                                         if (str_eq(p, exclude_name)) {
335                                                 /* It's a link to "ccache"! */
336                                                 free(p);
337                                                 free(buf);
338                                                 continue;
339                                         }
340                                         free(buf);
341                                         free(p);
342                                 }
343                         }
344
345                         /* Found it! */
346                         free(path);
347                         return fname;
348                 }
349                 free(fname);
350 #endif
351         }
352
353         free(path);
354         return NULL;
355 }
356
357 void
358 print_command(FILE *fp, char **argv)
359 {
360         int i;
361         for (i = 0; argv[i]; i++) {
362                 fprintf(fp, "%s%s",  (i == 0) ? "" : " ", argv[i]);
363         }
364         fprintf(fp, "\n");
365 }