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 #include <config.h>
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <string.h>
30 #include <fcntl.h>
31 #include <errno.h>
32
33 #include <sys/stat.h>
34 #include <sys/types.h>
35 #include <sys/wait.h>
36
37 #include "distcc.h"
38 #include "trace.h"
39 #include "exec.h"
40 #include "rpc.h"
41 #include "exitcode.h"
42 #include "util.h"
43 #include "clinet.h"
44 #include "bulk.h"
45 #include "hosts.h"
46 #include "state.h"
47 #include "include_server_if.h"
48 #include "emaillog.h"
49
50 /**
51 * @file
52 *
53 * @brief Client-side RPC functions.
54 **/
55
56 /*
57 * Transmit header for whole request.
58 */
dcc_x_req_header(int fd,enum dcc_protover protover)59 int dcc_x_req_header(int fd,
60 enum dcc_protover protover)
61 {
62 return dcc_x_token_int(fd, "DIST", protover);
63 }
64
65
66
67 /**
68 * Transmit an argv-type array.
69 **/
dcc_x_argv(int fd,const char * argc_token,const char * argv_token,char ** argv)70 int dcc_x_argv(int fd,
71 const char *argc_token,
72 const char *argv_token,
73 char **argv)
74 {
75 int i;
76 int ret;
77 int argc;
78
79 argc = dcc_argv_len(argv);
80
81 if (dcc_x_token_int(fd, argc_token, (unsigned) argc))
82 return EXIT_PROTOCOL_ERROR;
83
84 for (i = 0; i < argc; i++) {
85 if ((ret = dcc_x_token_string(fd, argv_token, argv[i])))
86 return ret;
87 }
88
89 return 0;
90 }
91
92 /**
93 * Transmit the current working directory
94 */
dcc_x_cwd(int fd)95 int dcc_x_cwd(int fd)
96 {
97 int ret;
98 char cwd[MAXPATHLEN + 1];
99 char * cwd_ret;
100 cwd_ret = getcwd(cwd, MAXPATHLEN);
101 if (cwd_ret == NULL) {
102 return 0;
103 }
104 ret = dcc_x_token_string(fd, "CDIR", cwd);
105 return ret;
106 }
107
108 /**
109 * Read the "DONE" token from the network that introduces a response.
110 **/
dcc_r_result_header(int ifd,enum dcc_protover expect_ver)111 int dcc_r_result_header(int ifd,
112 enum dcc_protover expect_ver)
113 {
114 unsigned vers;
115 int ret;
116
117 if ((ret = dcc_r_token_int(ifd, "DONE", &vers))) {
118 rs_log_error("server provided no answer. "
119 "Is the server configured to allow access from your IP"
120 " address? Is the server performing authentication and"
121 " your client isn't? Does the server have the compiler"
122 " installed? Is the server configured to access the"
123 " compiler?");
124 return ret;
125 }
126
127 if (vers != expect_ver) {
128 rs_log_error("got version %d not %d in response from server",
129 vers, expect_ver);
130 return EXIT_PROTOCOL_ERROR;
131 }
132
133 rs_trace("got response header");
134
135 return 0;
136 }
137
138
dcc_r_cc_status(int ifd,int * status)139 int dcc_r_cc_status(int ifd, int *status)
140 {
141 unsigned u_status;
142 int ret;
143
144 ret = dcc_r_token_int(ifd, "STAT", &u_status);
145 *status = u_status;
146 return ret;
147 }
148
149
150 /**
151 * The second half of the client protocol: retrieve all results from the server.
152 **/
dcc_retrieve_results(int net_fd,int * status,const char * output_fname,const char * deps_fname,const char * server_stderr_fname,struct dcc_hostdef * host)153 int dcc_retrieve_results(int net_fd,
154 int *status,
155 const char *output_fname,
156 const char *deps_fname,
157 const char *server_stderr_fname,
158 struct dcc_hostdef *host)
159 {
160 unsigned len;
161 int ret;
162 unsigned o_len;
163
164 if ((ret = dcc_r_result_header(net_fd, host->protover)))
165 return ret;
166
167 /* We've started to see the response, so the server is done
168 * compiling. */
169 dcc_note_state(DCC_PHASE_RECEIVE, NULL, NULL, DCC_REMOTE);
170
171 if ((ret = dcc_r_cc_status(net_fd, status)))
172 return ret;
173
174 if ((ret = dcc_r_token_int(net_fd, "SERR", &len)))
175 return ret;
176
177 /* Save the server-side errors into a file. This way, we can
178 decide later whether we want to report them to the user
179 or not. We don't want to report them to the user if
180 we are going to redo the compilation locally, because then
181 the local errors are going to appear.
182 Always put the server-side errors in the email we will
183 send to the maintainers, though.
184 */
185
186 if ((ret = dcc_r_file(net_fd, server_stderr_fname, len, host->compr)))
187 return ret;
188
189 if (dcc_add_file_to_log_email("server-side stderr", server_stderr_fname))
190 return ret;
191
192 if ((ret = dcc_r_token_int(net_fd, "SOUT", &len))
193 || (ret = dcc_r_bulk(STDOUT_FILENO, net_fd, len, host->compr))
194 || (ret = dcc_r_token_int(net_fd, "DOTO", &o_len)))
195 return ret;
196
197
198 /* If the compiler succeeded, then we always retrieve the result,
199 * even if it's 0 bytes. */
200 if (*status == 0) {
201 if ((ret = dcc_r_file_timed(net_fd, output_fname, o_len, host->compr)))
202 return ret;
203 if (host->cpp_where == DCC_CPP_ON_SERVER) {
204 if ((ret = dcc_r_token_int(net_fd, "DOTD", &len) == 0)
205 && deps_fname != NULL) {
206 ret = dcc_r_file_timed(net_fd, deps_fname, len, host->compr);
207 return ret;
208 }
209 }
210 } else if (o_len != 0) {
211 rs_log_error("remote compiler failed but also returned output: "
212 "I don't know what to do");
213 }
214
215 return 0;
216 }
217
218 /* points_to must be at least MAXPATHLEN + 1 long */
dcc_read_link(const char * fname,char * points_to)219 int dcc_read_link(const char* fname, char *points_to)
220 {
221 int len;
222 if ((len = readlink(fname, points_to, MAXPATHLEN)) == -1) {
223 rs_log_error("readlink '%s' failed: %s", fname, strerror(errno));
224 return EXIT_IO_ERROR;
225 }
226 points_to[len] = '\0';
227 return 0;
228 }
229
dcc_is_link(const char * fname,int * is_link)230 int dcc_is_link(const char *fname, int *is_link)
231 {
232 struct stat buf;
233
234 if (lstat(fname, &buf) == -1) {
235 rs_log_error("stat '%s' failed: %s", fname, strerror(errno));
236 return EXIT_IO_ERROR;
237 }
238
239 *is_link = S_ISLNK(buf.st_mode);
240 return 0;
241 }
242
243 /* Send to @p ofd @p n_files whose names are in @p fnames.
244 * @fnames must be null-terminated.
245 * The names can be coming from the include server, so
246 * we consult dcc_get_original_fname to get the real names.
247 * Always uses lzo compression.
248 */
249 /* TODO: This code is highly specific to DCC_VER_3; it assumes
250 lzo compression is on, and that the include server has
251 actually compressed the files. */
dcc_x_many_files(int ofd,unsigned int n_files,char ** fnames)252 int dcc_x_many_files(int ofd,
253 unsigned int n_files,
254 char **fnames)
255 {
256 int ret;
257 char link_points_to[MAXPATHLEN + 1];
258 int is_link;
259 const char *fname;
260 char *original_fname;
261
262 dcc_x_token_int(ofd, "NFIL", n_files);
263
264 for (; *fnames != NULL; ++fnames) {
265 fname = *fnames;
266 ret = dcc_get_original_fname(fname, &original_fname);
267 if (ret) return ret;
268
269 if ((ret = dcc_is_link(fname, &is_link))) {
270 return ret;
271 }
272
273 if (is_link) {
274 if ((ret = dcc_read_link(fname, link_points_to)) ||
275 (ret = dcc_x_token_string(ofd, "NAME", original_fname)) ||
276 (ret = dcc_x_token_string(ofd, "LINK", link_points_to))) {
277 return ret;
278 }
279 } else {
280 ret = dcc_x_token_string(ofd, "NAME", original_fname);
281 if (ret) return ret;
282 /* File should be compressed already.
283 If we ever support non-compressed server-side-cpp,
284 we should have some checks here and then uncompress
285 the file if it is compressed. */
286 ret = dcc_x_file(ofd, fname, "FILE", DCC_COMPRESS_NONE,
287 NULL);
288 if (ret) return ret;
289 }
290 }
291 return 0;
292 }
293