1 /* -*- c-file-style: "java"; indent-tabs-mode: nil; tab-width: 4; fill-column: 78 -*-
2  *
3  * distcc -- A simple distributed compiler system
4  *
5  * Copyright (C) 2002, 2003, 2004 by Martin Pool <mbp@samba.org>
6  * Copyright 2007 Google Inc.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
21  * USA.
22  */
23 
24 
25             /* 18 Their bows also shall dash the young men
26              * to pieces; and they shall have no pity on
27              * the fruit of the womb; their eyes shall not
28              * spare children.
29              *        -- Isaiah 13 */
30 
31 /**
32  * @file
33  *
34  * Run compilers or preprocessors.
35  *
36  * The whole server is run in a separate process group and normally in a
37  * separate session.  (It is not a separate session in --no-detach debug
38  * mode.)  This allows us to cleanly kill off all children and all compilers
39  * when the parent is terminated.
40  *
41  * @todo On Cygwin, fork() must be emulated and therefore will be
42  * slow.  It would be faster to just use their spawn() call, rather
43  * than fork/exec.
44  **/
45 
46 #include <config.h>
47 
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <unistd.h>
51 #include <string.h>
52 #include <fcntl.h>
53 #include <errno.h>
54 #include <poll.h>
55 #include <signal.h>
56 
57 #include <sys/types.h>
58 #include <sys/wait.h>
59 #include <sys/stat.h>
60 #include <sys/time.h>
61 #include <sys/resource.h>
62 
63 #ifdef __CYGWIN__
64     /* #define NOGDI */
65     #define RC_INVOKED
66     #define NOWINRES
67     #include <windows.h>
68 #endif
69 
70 #include "distcc.h"
71 #include "trace.h"
72 #include "util.h"
73 #include "exitcode.h"
74 #include "exec.h"
75 #include "lock.h"
76 #include "hosts.h"
77 #include "dopt.h"
78 
79 const int timeout_null_fd = -1;
80 int dcc_job_lifetime = 0;
81 
82 static void dcc_inside_child(char **argv,
83                              const char *stdin_file,
84                              const char *stdout_file,
85                              const char *stderr_file) NORETURN;
86 
87 
88 static void dcc_execvp(char **argv) NORETURN;
89 
dcc_note_execution(struct dcc_hostdef * host,char ** argv)90 void dcc_note_execution(struct dcc_hostdef *host, char **argv)
91 {
92     char *astr;
93 
94     astr = dcc_argv_tostr(argv);
95     rs_log(RS_LOG_INFO|RS_LOG_NONAME, "exec on %s: %s",
96            host->hostdef_string, astr);
97     free(astr);
98 }
99 
100 
101 /**
102  * Redirect stdin/out/err.  Filenames may be NULL to leave them untouched.
103  *
104  * This is called when running a job remotely, but *not* when running
105  * it locally, because people might e.g. want cpp to read from stdin.
106  **/
dcc_redirect_fds(const char * stdin_file,const char * stdout_file,const char * stderr_file)107 int dcc_redirect_fds(const char *stdin_file,
108                      const char *stdout_file,
109                      const char *stderr_file)
110 {
111     int ret;
112 
113     if (stdin_file)
114         if ((ret = dcc_redirect_fd(STDIN_FILENO, stdin_file, O_RDONLY)))
115             return ret;
116 
117     if (stdout_file) {
118         if ((ret = dcc_redirect_fd(STDOUT_FILENO, stdout_file,
119                                    O_WRONLY | O_CREAT | O_TRUNC)))
120             return ret;
121     }
122 
123     if (stderr_file) {
124         /* Open in append mode, because the server will dump its own error
125          * messages into the compiler's error file.  */
126         if ((ret = dcc_redirect_fd(STDERR_FILENO, stderr_file,
127                                    O_WRONLY | O_CREAT | O_APPEND)))
128             return ret;
129     }
130 
131     return 0;
132 }
133 
134 
135 #ifdef __CYGWIN__
136 /* Execute a process WITHOUT console window and correctly redirect output. */
dcc_execvp_cyg(char ** argv,const char * input_file,const char * output_file,const char * error_file)137 static DWORD dcc_execvp_cyg(char **argv, const char *input_file,
138     const char *output_file, const char *error_file)
139 {
140     STARTUPINFO    m_siStartInfo;
141     PROCESS_INFORMATION m_piProcInfo;
142     char cmdline[MAX_PATH+1]={0};
143     HANDLE stdin_hndl=INVALID_HANDLE_VALUE;
144     HANDLE stdout_hndl=INVALID_HANDLE_VALUE;
145     HANDLE stderr_hndl=INVALID_HANDLE_VALUE;
146     char **ptr;
147     DWORD exit_code;
148     BOOL bRet=0;
149 
150     ZeroMemory(&m_siStartInfo, sizeof(STARTUPINFO));
151     ZeroMemory( &m_piProcInfo, sizeof(PROCESS_INFORMATION) );
152 
153     /* Open files for IO redirection */
154     if (input_file && strcmp(input_file,"/dev/null")!=0)
155     {
156         if ((stdin_hndl = CreateFile(input_file,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_ALWAYS,
157             FILE_ATTRIBUTE_TEMPORARY,NULL)) == INVALID_HANDLE_VALUE) {
158             exit_code = GetLastError();
159             goto cleanup;
160         }
161     } else
162         stdin_hndl = GetStdHandle(STD_INPUT_HANDLE);
163 
164     if (output_file && strcmp(output_file,"/dev/null")!=0)
165     {
166         if ((stdout_hndl = CreateFile(output_file,GENERIC_WRITE,FILE_SHARE_READ,NULL,
167             CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY,NULL)) == INVALID_HANDLE_VALUE) {
168             exit_code = GetLastError();
169             goto cleanup;
170         }
171     } else
172         stdout_hndl = GetStdHandle(STD_OUTPUT_HANDLE);
173 
174     if (error_file && strcmp(error_file,"/dev/null")!=0)
175     {
176         if ((stderr_hndl = CreateFile(error_file, GENERIC_WRITE,
177             FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,
178             OPEN_ALWAYS, FILE_ATTRIBUTE_TEMPORARY,NULL)) == INVALID_HANDLE_VALUE) {
179             exit_code = GetLastError();
180             goto cleanup;
181         }
182         /* Seek to the end of file (ignore return code) */
183         SetFilePointer(stderr_hndl,0,NULL,FILE_END);
184 
185     } else
186         stderr_hndl = GetStdHandle(STD_ERROR_HANDLE);
187 
188     /* Ensure handles can be inherited */
189     SetHandleInformation(stdin_hndl,HANDLE_FLAG_INHERIT,HANDLE_FLAG_INHERIT);
190     SetHandleInformation(stdout_hndl,HANDLE_FLAG_INHERIT,HANDLE_FLAG_INHERIT);
191     SetHandleInformation(stderr_hndl,HANDLE_FLAG_INHERIT,HANDLE_FLAG_INHERIT);
192 
193     /*Set up members of STARTUPINFO structure.*/
194     m_siStartInfo.cb = sizeof(STARTUPINFO);
195     m_siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
196     m_siStartInfo.wShowWindow = SW_HIDE;
197     m_siStartInfo.hStdInput = stdin_hndl;
198     m_siStartInfo.hStdOutput = stdout_hndl;
199     m_siStartInfo.hStdError = stderr_hndl;
200 
201     /* Create command line */
202     for (ptr=argv;*ptr!=NULL;ptr++)
203     {
204         strcat(cmdline, *ptr);
205         strcat(cmdline, " ");
206     }
207 
208     /* Create the child process.  */
209     bRet = CreateProcess(NULL,
210         cmdline,        /* application name */
211         NULL,           /* process security attributes */
212         NULL,           /* primary thread security attributes */
213         TRUE,           /* handles are inherited */
214         CREATE_NEW_CONSOLE, /* creation flags */
215         NULL,           /* use parent's environment */
216         NULL,           /* use parent's current directory */
217         &m_siStartInfo,  /* STARTUPINFO pointer */
218         &m_piProcInfo);  /* receives PROCESS_INFORMATION */
219     if (!bRet) {
220         exit_code = GetLastError();
221         goto cleanup;
222     }
223 
224     WaitForSingleObject(m_piProcInfo.hProcess, (DWORD)(-1L));
225     /* return termination code and exit code*/
226     GetExitCodeProcess(m_piProcInfo.hProcess, &exit_code);
227     CloseHandle(m_piProcInfo.hProcess);
228 
229     /* We can get here only if process creation failed */
230     cleanup:
231     if (stdin_hndl != INVALID_HANDLE_VALUE) CloseHandle(stdin_hndl);
232     if (stdout_hndl != INVALID_HANDLE_VALUE) CloseHandle(stdout_hndl);
233     if (stderr_hndl != INVALID_HANDLE_VALUE) CloseHandle(stderr_hndl);
234 
235     if (bRet)
236         ExitProcess(exit_code); /* Return cmdline's exit-code to parent process */
237     else
238         return exit_code;       /* Return failure reason to calling fn */
239 }
240 #endif
241 
242 /**
243  * Replace this program with another in the same process.
244  *
245  * Does not return, either execs the compiler in place, or exits with
246  * a message.
247  **/
dcc_execvp(char ** argv)248 static void dcc_execvp(char **argv)
249 {
250     char *slash;
251 
252     execvp(argv[0], argv);
253 
254     /* If we're still running, the program was not found on the path.  One
255      * thing that might have happened here is that the client sent an absolute
256      * compiler path, but the compiler's located somewhere else on the server.
257      * In the absence of anything better to do, we search the path for its
258      * basename.
259      *
260      * Actually this code is called on both the client and server, which might
261      * cause unintnded behaviour in contrived cases, like giving a full path
262      * to a file that doesn't exist.  I don't think that's a problem. */
263 
264     slash = strrchr(argv[0], '/');
265     if (slash)
266         execvp(slash + 1, argv);
267 
268     /* shouldn't be reached */
269     rs_log_error("failed to exec %s: %s", argv[0], strerror(errno));
270 
271     dcc_exit(EXIT_COMPILER_MISSING); /* a generalization, i know */
272 }
273 
274 
275 
276 /**
277  * Called inside the newly-spawned child process to execute a command.
278  * Either executes it, or returns an appropriate error.
279  *
280  * This routine also takes a lock on localhost so that it's counted
281  * against the process load.  That lock will go away when the process
282  * exits.
283  *
284  * In this current version locks are taken without regard to load limitation
285  * on the current machine.  The main impact of this is that cpp running on
286  * localhost will cause jobs to be preferentially distributed away from
287  * localhost, but it should never cause the machine to deadlock waiting for
288  * localhost slots.
289  *
290  * @param what Type of process to be run here (cpp, cc, ...)
291  **/
dcc_inside_child(char ** argv,const char * stdin_file,const char * stdout_file,const char * stderr_file)292 static void dcc_inside_child(char **argv,
293                              const char *stdin_file,
294                              const char *stdout_file,
295                              const char *stderr_file)
296 {
297     int ret;
298 
299     if ((ret = dcc_ignore_sigpipe(0)))
300         goto fail;              /* set handler back to default */
301 
302     /* Ignore failure */
303     dcc_increment_safeguard();
304 
305 #ifdef __CYGWIN__
306     /* This will execute compiler and CORRECTLY redirect output if compiler is
307      * a native Windows application.  If this never returns, it means the
308      * compiler-execute succeeded.  We use a hack to decide if it's a windows
309      * application: if argv[0] starts with "<letter>:" or with "\\", then it's
310      * a windows path and we try dcc_execvp_cyg.  If not, we assume it's a
311      * cygwin app and fall through to the unix-style forking, below.  If we
312      * guess wrong, dcc_execvp_cyg will probably fail with error 3
313      * (windows-exe for "path not found"), so again we'll fall through to the
314      * unix-fork case.  Otherwise we just fail in a generic way.
315      * TODO(csilvers): Figure out the right way to deal with this.  Running
316      *                 cygwin apps via dcc_execvp_cyg segfaults (and takes a
317      *                 long time to do it too), so I want to avoid that if
318      *                 possible.  I don't know enough about cygwin or
319      *                 cygwin/windows interactions to know the right thing to
320      *                 do here.  Until distcc has cl.exe support, this may
321      *                 all be a moot point anyway.
322      */
323     if (argv[0] && ((argv[0][0] != '\0' && argv[0][1] == ':') ||
324                     (argv[0][0] == '\\' && argv[0][1] == '\\'))) {
325         DWORD status;
326         status = dcc_execvp_cyg(argv, stdin_file, stdout_file, stderr_file);
327         if (status != 3) {
328             ret = EXIT_DISTCC_FAILED;
329             goto fail;
330         }
331     }
332 #endif
333 
334     /* do this last, so that any errors from previous operations are
335      * visible */
336     if ((ret = dcc_redirect_fds(stdin_file, stdout_file, stderr_file)))
337         goto fail;
338 
339     dcc_execvp(argv);
340 
341     ret = EXIT_DISTCC_FAILED;
342 
343     fail:
344     dcc_exit(ret);
345 }
346 
347 
dcc_new_pgrp(void)348 int dcc_new_pgrp(void)
349 {
350     /* If we're a session group leader, then we are not able to call
351      * setpgid().  However, setsid will implicitly have put us into a new
352      * process group, so we don't have to do anything. */
353 
354     /* Does everyone have getpgrp()?  It's in POSIX.1.  We used to call
355      * getpgid(0), but that is not available on BSD/OS. */
356     if (getpgrp() == getpid()) {
357         rs_trace("already a process group leader");
358         return 0;
359     }
360 
361     if (setpgid(0, 0) == 0) {
362         rs_trace("entered process group");
363         return 0;
364     } else {
365         rs_trace("setpgid(0, 0) failed: %s", strerror(errno));
366         return EXIT_DISTCC_FAILED;
367     }
368 }
369 
370 
371 /**
372  * Run @p argv in a child asynchronously.
373  *
374  * stdin, stdout and stderr are redirected as shown, unless those
375  * filenames are NULL.  In that case they are left alone.
376  *
377  * @warning When called on the daemon, where stdin/stdout may refer to random
378  * network sockets, all of the standard file descriptors must be redirected!
379  **/
dcc_spawn_child(char ** argv,pid_t * pidptr,const char * stdin_file,const char * stdout_file,const char * stderr_file)380 int dcc_spawn_child(char **argv, pid_t *pidptr,
381                     const char *stdin_file,
382                     const char *stdout_file,
383                     const char *stderr_file)
384 {
385     pid_t pid;
386 
387     dcc_trace_argv("forking to execute", argv);
388 
389     pid = fork();
390     if (pid == -1) {
391         rs_log_error("failed to fork: %s", strerror(errno));
392         return EXIT_OUT_OF_MEMORY; /* probably */
393     } else if (pid == 0) {
394         /* If this is a remote compile,
395          * put the child in a new group, so we can
396          * kill it and all its descendents without killing distccd
397          * FIXME: if you kill distccd while it's compiling, and
398          * the compiler has an infinite loop bug, the new group
399          * will run forever until you kill it.
400          */
401         if (stdout_file != NULL) {
402             if (dcc_new_pgrp() != 0)
403                 rs_trace("Unable to start a new group\n");
404         }
405         dcc_inside_child(argv, stdin_file, stdout_file, stderr_file);
406         /* !! NEVER RETURN FROM HERE !! */
407     } else {
408         *pidptr = pid;
409         rs_trace("child started as pid%d", (int) pid);
410         return 0;
411     }
412 }
413 
414 
dcc_reset_signal(int whichsig)415 void dcc_reset_signal(int whichsig)
416 {
417     struct sigaction act_dfl;
418 
419     memset(&act_dfl, 0, sizeof act_dfl);
420     act_dfl.sa_handler = SIG_DFL;
421     sigaction(whichsig, &act_dfl, NULL);
422     /* might be called from signal handler, therefore no IO to log a
423      * message */
424 }
425 
426 
sys_wait4(pid_t pid,int * status,int options,struct rusage * rusage)427 static int sys_wait4(pid_t pid, int *status, int options, struct rusage *rusage)
428 {
429 
430     /* Prefer use waitpid to wait4 for non-blocking wait with WNOHANG option */
431 #ifdef HAVE_WAITPID
432     /* Just doing getrusage(children) is not sufficient, because other
433      * children may have exited previously. */
434     memset(rusage, 0, sizeof *rusage);
435     return waitpid(pid, status, options);
436 #elif HAVE_WAIT4
437     return wait4(pid, status, options, rusage);
438 #else
439 #error Please port this
440 #endif
441 }
442 
443 
444 /**
445  * Blocking wait for a child to exit.  This is used when waiting for
446  * cpp, gcc, etc.
447  *
448  * This is not used by the daemon-parent; it has its own
449  * implementation in dcc_reap_kids().  They could be unified, but the
450  * parent only waits when it thinks a child has exited; the child
451  * waits all the time.
452  **/
dcc_collect_child(const char * what,pid_t pid,int * wait_status,int in_fd)453 int dcc_collect_child(const char *what, pid_t pid,
454                       int *wait_status, int in_fd)
455 {
456     struct rusage ru;
457     pid_t ret_pid;
458 
459     int ret;
460     int wait_timeout_sec;
461     fd_set fds,readfds;
462 
463     wait_timeout_sec = dcc_job_lifetime;
464 
465     FD_ZERO(&readfds);
466     if (in_fd != timeout_null_fd){
467         FD_SET(in_fd,&readfds);
468     }
469 
470 
471     while (!dcc_job_lifetime || wait_timeout_sec-- >= 0) {
472 
473         /* If we're called with a socket, break out of the loop if the socket
474          * disconnects. To do that, we need to block in select, not in
475          * sys_wait4.  (Only waitpid uses WNOHANG to mean don't block ever,
476          * so I've modified sys_wait4 above to preferentially call waitpid.)
477          */
478         int flags = (in_fd == timeout_null_fd) ? 0 : WNOHANG;
479         ret_pid = sys_wait4(pid, wait_status, flags, &ru);
480 
481         if (ret_pid == -1) {
482             if (errno == EINTR) {
483                 rs_trace("wait4 was interrupted; retrying");
484             } else {
485                 rs_log_error("sys_wait4(pid=%d) borked: %s", (int) pid,
486                              strerror(errno));
487                 return EXIT_DISTCC_FAILED;
488             }
489         } else if (ret_pid != 0) {
490             /* This is not the main user-visible message; that comes from
491              * critique_status(). */
492             rs_trace("%s child %ld terminated with status %#x",
493                      what, (long) ret_pid, *wait_status);
494             rs_log_info("%s times: user %lld.%06lds, system %lld.%06lds, "
495                         "%ld minflt, %ld majflt",
496                         what,
497                         (long long) ru.ru_utime.tv_sec, (long) ru.ru_utime.tv_usec,
498                         (long long) ru.ru_stime.tv_sec, (long) ru.ru_stime.tv_usec,
499                         ru.ru_minflt, ru.ru_majflt);
500 
501             return 0;
502         }
503 
504         /* check timeout */
505         if (in_fd != timeout_null_fd) {
506             struct timeval timeout;
507 
508             /* If client disconnects, the socket will become readable,
509              * and a read should return -1 and set errno to EPIPE.
510              */
511             fds = readfds;
512             timeout.tv_sec = 1;
513             timeout.tv_usec = 0;
514             ret = select(in_fd+1,&fds,NULL,NULL,&timeout);
515             if (ret == 1) {
516                 char buf;
517                 int nread = read(in_fd, &buf, 1);
518                 if ((nread == -1) && (errno == EWOULDBLOCK)) {
519                     /* spurious wakeup, ignore */
520                     ;
521                 } else if (nread == 0) {
522                     rs_log_error("Client fd disconnected, killing job");
523                     /* If killpg fails, it might means the child process is not
524                      * in a new group, so, just kill the child process */
525                     if (killpg(pid,SIGTERM)!=0)
526                         kill(pid, SIGTERM);
527                     return EXIT_IO_ERROR;
528                 } else if (nread == 1) {
529                     rs_log_error("Bug!  Read from fd succeeded when checking "
530                                  "whether client disconnected!");
531                 } else {
532                     rs_log_error("Bug!  nread %d, errno %d checking whether "
533                                  "client disconnected!", nread, errno);
534                 }
535             }
536         } else {
537             poll(NULL, 0, 1000);
538         }
539     }
540     /* If timeout, also kill the child process */
541     if (killpg(pid, SIGTERM) != 0)
542         kill(pid, SIGTERM);
543     rs_log_error("Compilation takes too long, timeout.");
544 
545     return EXIT_TIMEOUT;
546 }
547 
548 
549 
550 /**
551  * Analyze and report to the user on a command's exit code.
552  *
553  * @param command short human-readable description of the command (perhaps
554  * argv[0])
555  *
556  * @returns 0 if the command succeeded; 128+SIGNAL if it stopped on a
557  * signal; otherwise the command's exit code.
558  **/
dcc_critique_status(int status,const char * command,const char * input_fname,struct dcc_hostdef * host,int verbose)559 int dcc_critique_status(int status,
560                         const char *command,
561                         const char *input_fname,
562                         struct dcc_hostdef *host,
563                         int verbose)
564 {
565     int logmode;
566 
567     /* verbose mode is only used for executions that the user is likely to
568      * particularly need to know about */
569     if (verbose)
570         logmode = RS_LOG_ERR | RS_LOG_NONAME;
571     else
572         logmode = RS_LOG_INFO | RS_LOG_NONAME;
573 
574     if (input_fname == NULL)
575         input_fname = "(null)";
576 
577     if (WIFSIGNALED(status)) {
578 #ifdef HAVE_STRSIGNAL
579         rs_log(logmode,
580                "%s %s on %s: %s%s",
581                command, input_fname, host->hostdef_string,
582                strsignal(WTERMSIG(status)),
583                WCOREDUMP(status) ? " (core dumped)" : "");
584 #else
585         rs_log(logmode,
586                "%s %s on %s terminated by signal %d%s",
587                command, input_fname, host->hostdef_string,
588                WTERMSIG(status),
589                WCOREDUMP(status) ? " (core dumped)" : "");
590 #endif
591         /* Unix convention is to return 128+signal when a subprocess crashes. */
592         return 128 + WTERMSIG(status);
593     } else if (WEXITSTATUS(status) == 1) {
594         /* Normal failure gives exit code 1, so handle that specially */
595         rs_log(logmode, "%s %s on %s failed", command, input_fname, host->hostdef_string);
596         return WEXITSTATUS(status);
597     } else if (WEXITSTATUS(status)) {
598         /* This is a tough call; we don't really want to clutter the client's
599          * error stream, but if we don't say where the compilation failed then
600          * people may find it hard to work things out. */
601 
602         rs_log(logmode,
603                "%s %s on %s failed with exit code %d",
604                command, input_fname, host->hostdef_string, WEXITSTATUS(status));
605         return WEXITSTATUS(status);
606     } else {
607         rs_log(RS_LOG_INFO|RS_LOG_NONAME,
608                "%s %s on %s completed ok", command, input_fname, host->hostdef_string);
609         return 0;
610     }
611 }
612