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