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