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