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
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 * Send a compilation request to a remote server.
26 */
27
28
29 #include <config.h>
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <string.h>
35 #include <fcntl.h>
36 #include <errno.h>
37
38 #include <sys/types.h>
39 #include <sys/time.h>
40
41 #include "distcc.h"
42 #include "trace.h"
43 #include "rpc.h"
44 #include "exitcode.h"
45 #include "util.h"
46 #include "clinet.h"
47 #include "hosts.h"
48 #include "exec.h"
49 #include "lock.h"
50 #include "compile.h"
51 #include "bulk.h"
52 #ifdef HAVE_GSSAPI
53 #include "auth.h"
54
55 /* Global security context in case confidentiality/integrity */
56 /* type services are needed in the future. */
57 extern gss_ctx_id_t distcc_ctx_handle;
58 #endif
59
60 /*
61 * TODO: If cpp finishes early and fails then perhaps break out of
62 * trying to connect.
63 *
64 * TODO: If we abort, perhaps kill the SSH child rather than closing
65 * the socket. Closing while a lot of stuff has been written through
66 * might make us block until the other side reads all the data.
67 */
68
69 /**
70 * Open a connection using either a TCP socket or SSH. Return input
71 * and output file descriptors (which may or may not be different.)
72 **/
dcc_remote_connect(struct dcc_hostdef * host,int * to_net_fd,int * from_net_fd,pid_t * ssh_pid)73 static int dcc_remote_connect(struct dcc_hostdef *host,
74 int *to_net_fd,
75 int *from_net_fd,
76 pid_t *ssh_pid)
77 {
78 int ret;
79
80 if (host->mode == DCC_MODE_TCP) {
81 *ssh_pid = 0;
82 if ((ret = dcc_connect_by_name(host->hostname, host->port,
83 to_net_fd)) != 0)
84 return ret;
85 *from_net_fd = *to_net_fd;
86 return 0;
87 } else if (host->mode == DCC_MODE_SSH) {
88 if ((ret = dcc_ssh_connect(NULL, host->user, host->hostname,
89 host->ssh_command,
90 from_net_fd, to_net_fd,
91 ssh_pid)))
92 return ret;
93 return 0;
94 } else {
95 rs_log_crit("impossible host mode");
96 return EXIT_DISTCC_FAILED;
97 }
98 }
99
100
dcc_wait_for_cpp(pid_t cpp_pid,int * status,const char * input_fname)101 static int dcc_wait_for_cpp(pid_t cpp_pid,
102 int *status,
103 const char *input_fname)
104 {
105 int ret;
106
107 if (cpp_pid) {
108 dcc_note_state(DCC_PHASE_CPP, NULL, NULL, DCC_LOCAL);
109 /* Wait for cpp to finish (if not already done), check the
110 * result, then send the .i file */
111
112 if ((ret = dcc_collect_child("cpp", cpp_pid, status, timeout_null_fd)))
113 return ret;
114
115 /* Although cpp failed, there is no need to try running the command
116 * locally, because we'd presumably get the same result. Therefore
117 * critique the command and log a message and return an indication
118 * that compilation is complete. */
119 if (dcc_critique_status(*status, "cpp", input_fname, dcc_hostdef_local, 0))
120 return 0;
121 }
122 return 0;
123 }
124
125
126 /* Send a request across to the already-open server.
127 *
128 * CPP_PID is the PID of the preprocessor running in the background.
129 * We wait for it to complete before reading its output.
130 */
131 static int
dcc_send_header(int net_fd,char ** argv,struct dcc_hostdef * host)132 dcc_send_header(int net_fd,
133 char **argv,
134 struct dcc_hostdef *host)
135 {
136 int ret;
137
138 tcp_cork_sock(net_fd, 1);
139
140 if ((ret = dcc_x_req_header(net_fd, host->protover)))
141 return ret;
142 if (host->cpp_where == DCC_CPP_ON_SERVER) {
143 if ((ret = dcc_x_cwd(net_fd)))
144 return ret;
145 }
146 if ((ret = dcc_x_argv(net_fd, "ARGC", "ARGV", argv)))
147 return ret;
148
149 return 0;
150 }
151
152
153 /**
154 * Pass a compilation across the network.
155 *
156 * When this function is called, the preprocessor has already been
157 * started in the background. It may have already completed, or it
158 * may still be running. The goal is that preprocessing will overlap
159 * with setting up the network connection, which may take some time
160 * but little CPU.
161 *
162 * If this function fails, compilation will be retried on the local
163 * machine.
164 *
165 * @param argv Compiler command to run.
166 *
167 * @param cpp_fname Filename of preprocessed source. May not be complete yet,
168 * depending on @p cpp_pid.
169 *
170 * @param files If we are doing preprocessing on the server, the names of
171 * all the files needed; otherwise, NULL.
172 *
173 * @param output_fname File that the object code should be delivered to.
174 *
175 * @param cpp_pid If nonzero, the pid of the preprocessor. Must be
176 * allowed to complete before we send the input file.
177 *
178 * @param local_cpu_lock_fd If != -1, file descriptor for the lock file.
179 * Should be != -1 iff (host->cpp_where != DCC_CPP_ON_SERVER).
180 * If != -1, the lock must be held on entry to this function,
181 * and THIS FUNCTION WILL RELEASE THE LOCK.
182 *
183 * @param host Definition of host to send this job to.
184 *
185 * @param status on return contains the wait-status of the remote
186 * compiler.
187 *
188 * Returns 0 on success, otherwise error. Returning nonzero does not
189 * necessarily imply the remote compiler itself succeeded, only that
190 * there were no communications problems.
191 *
192 * TODO: consider refactoring this (perhaps as two separate subroutines?)
193 * to avoid the need for releasing the lock as a side effect of this call.
194 */
dcc_compile_remote(char ** argv,char * input_fname,char * cpp_fname,char ** files,char * output_fname,char * deps_fname,char * server_stderr_fname,pid_t cpp_pid,int local_cpu_lock_fd,struct dcc_hostdef * host,int * status)195 int dcc_compile_remote(char **argv,
196 char *input_fname,
197 char *cpp_fname,
198 char **files,
199 char *output_fname,
200 char *deps_fname,
201 char *server_stderr_fname,
202 pid_t cpp_pid,
203 int local_cpu_lock_fd,
204 struct dcc_hostdef *host,
205 int *status)
206 {
207 int to_net_fd = -1, from_net_fd = -1;
208 int ret;
209 pid_t ssh_pid = 0;
210 int ssh_status;
211 off_t doti_size;
212 struct timeval before, after;
213 unsigned int n_files;
214
215 if (gettimeofday(&before, NULL))
216 rs_log_warning("gettimeofday failed");
217
218 dcc_note_execution(host, argv);
219 dcc_note_state(DCC_PHASE_CONNECT, input_fname, host->hostname, DCC_REMOTE);
220
221 /* For ssh support, we need to allow for separate fds writing to and
222 * reading from the network, because our connection to the ssh client may
223 * be over pipes, which are one-way connections. */
224
225 *status = 0;
226 if ((ret = dcc_remote_connect(host, &to_net_fd, &from_net_fd, &ssh_pid)))
227 goto out;
228
229 #ifdef HAVE_GSSAPI
230 /* Perform requested security. */
231 if(host->authenticate) {
232 rs_log_info("Performing authentication.");
233
234 if ((ret = dcc_gssapi_perform_requested_security(host, to_net_fd, from_net_fd)) != 0) {
235 rs_log_crit("Failed to perform authentication.");
236 goto out;
237 }
238
239 /* Context deleted here as we no longer need it. However, we have it available */
240 /* in case we want to use confidentiality/integrity type services in the future. */
241 dcc_gssapi_delete_ctx(&distcc_ctx_handle);
242 } else {
243 rs_log_info("No authentication requested.");
244 }
245 #endif
246
247 dcc_note_state(DCC_PHASE_SEND, NULL, NULL, DCC_REMOTE);
248
249 if (host->cpp_where == DCC_CPP_ON_SERVER) {
250 if ((ret = dcc_send_header(to_net_fd, argv, host))) {
251 goto out;
252 }
253
254 n_files = dcc_argv_len(files);
255 if ((ret = dcc_x_many_files(to_net_fd, n_files, files))) {
256 goto out;
257 }
258 } else {
259 /* This waits for cpp and puts its status in *status. If cpp failed,
260 * then the connection will have been dropped and we need not bother
261 * trying to get any response from the server. */
262
263 if ((ret = dcc_send_header(to_net_fd, argv, host)))
264 goto out;
265
266 if ((ret = dcc_wait_for_cpp(cpp_pid, status, input_fname)))
267 goto out;
268
269 /* We are done with local preprocessing. Unlock to allow someone
270 * else to start preprocessing. */
271 if (local_cpu_lock_fd != -1) {
272 dcc_unlock(local_cpu_lock_fd);
273 local_cpu_lock_fd = -1;
274 }
275
276 if (*status != 0)
277 goto out;
278
279 if ((ret = dcc_x_file(to_net_fd, cpp_fname, "DOTI", host->compr,
280 &doti_size)))
281 goto out;
282 }
283
284 rs_trace("client finished sending request to server");
285 tcp_cork_sock(to_net_fd, 0);
286 /* but it might not have been read in by the server yet; there's
287 * 100kB or more of buffers in the two kernels. */
288
289 /* OK, now all of the source has at least made it into the
290 * client's TCP transmission queue, sometime soon the server will
291 * start compiling it. */
292 dcc_note_state(DCC_PHASE_COMPILE, NULL, host->hostname, DCC_REMOTE);
293
294 /* If cpp failed, just abandon the connection, without trying to
295 * receive results. */
296 if (ret == 0 && *status == 0) {
297 ret = dcc_retrieve_results(from_net_fd, status, output_fname,
298 deps_fname, server_stderr_fname, host);
299 }
300
301 if (gettimeofday(&after, NULL)) {
302 rs_log_warning("gettimeofday failed");
303 } else if (host->cpp_where == DCC_CPP_ON_CLIENT) {
304 double secs, rate;
305
306 dcc_calc_rate(doti_size, &before, &after, &secs, &rate);
307 rs_log(RS_LOG_INFO|RS_LOG_NONAME,
308 "%lu bytes from %s compiled on %s in %.4fs, rate %.0fkB/s",
309 (unsigned long) doti_size, input_fname, host->hostname,
310 secs, rate);
311 }
312
313 out:
314 if (local_cpu_lock_fd != -1) {
315 dcc_unlock(local_cpu_lock_fd);
316 local_cpu_lock_fd = -1; /* Not really needed; just for consistency. */
317 }
318
319 /* Close socket so that the server can terminate, rather than
320 * making it wait until we've finished our work. */
321 if (to_net_fd != from_net_fd) {
322 if (to_net_fd != -1)
323 dcc_close(to_net_fd);
324 }
325 if (from_net_fd != -1)
326 dcc_close(from_net_fd);
327
328 /* Collect the SSH child. Strictly this is unnecessary; it might slow the
329 * client down a little when things could otherwise be proceeding in the
330 * background. But it helps make sure that we don't assume we succeeded
331 * when something possibly went wrong, and it allows us to account for the
332 * cost of the ssh child. */
333 if (ssh_pid) {
334 dcc_collect_child("ssh", ssh_pid, &ssh_status, timeout_null_fd); /* ignore failure */
335 }
336
337 return ret;
338 }
339