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 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             /* 15 Every one that is found shall be thrust
26              * through; and every one that is joined unto
27              * them shall fall by the sword.
28              *        -- Isaiah 13 */
29 
30 
31 #include <config.h>
32 
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <string.h>
37 #include <fcntl.h>
38 #include <errno.h>
39 #include <ctype.h>
40 #include <stdint.h>
41 
42 #include <sys/stat.h>
43 
44 #include "distcc.h"
45 #include "trace.h"
46 #include "exitcode.h"
47 #include "rpc.h"
48 #include "snprintf.h"
49 
50 
51 /**
52  * @file
53  *
54  * Very simple RPC-like layer.  Requests and responses are build of
55  * little packets each containing a 4-byte ascii token, an 8-byte hex
56  * value or length, and optionally data corresponding to the length.
57  *
58  * 'x' means transmit, and 'r' means receive.
59  *
60  * This builds on top of io.c and is called by the various routines
61  * that handle communication.
62  **/
63 
64 
65 /**
66  * Transmit token name (4 characters) and value (32-bit int, as 8 hex
67  * characters).
68  **/
dcc_x_token_int(int ofd,const char * token,unsigned param)69 int dcc_x_token_int(int ofd, const char *token, unsigned param)
70 {
71     char buf[13];
72     int shift;
73     char *p;
74     const char *hex = "0123456789abcdef";
75 
76     if (strlen(token) != 4) {
77         rs_log_crit("token \"%s\" seems wrong", token);
78         return EXIT_PROTOCOL_ERROR;
79     }
80     memcpy(buf, token, 4);
81 
82     /* Quick and dirty int->hex.  The only standard way is to call snprintf
83      * (?), which is undesirably slow for such a frequently-called
84      * function. */
85     for (shift=28, p = &buf[4];
86          shift >= 0;
87          shift -= 4, p++) {
88         *p = hex[(param >> shift) & 0xf];
89     }
90     buf[12] = '\0';
91 
92     rs_trace("send %s", buf);
93     return dcc_writex(ofd, buf, 12);
94 }
95 
96 
97 /**
98  * Send start of a result: DONE <version>
99  **/
dcc_x_result_header(int ofd,enum dcc_protover protover)100 int dcc_x_result_header(int ofd,
101                         enum dcc_protover protover)
102 {
103     return dcc_x_token_int(ofd, "DONE", protover);
104 }
105 
106 
dcc_x_cc_status(int ofd,int status)107 int dcc_x_cc_status(int ofd, int status)
108 {
109     return dcc_x_token_int(ofd, "STAT", (unsigned) status);
110 }
111 
112 
dcc_r_token(int ifd,char * buf)113 int dcc_r_token(int ifd, char *buf)
114 {
115     return dcc_readx(ifd, buf, 4);
116 }
117 
118 
119 /**
120  * We got a mismatch on a token, which indicates either a bug in distcc, or
121  * that somebody (inetd?) is interfering with our network stream, or perhaps
122  * some other network problem.  Whatever's happened, a bit more debugging
123  * information would be handy.
124  **/
dcc_explain_mismatch(const char * buf,size_t buflen,int ifd)125 int dcc_explain_mismatch(const char *buf,
126                          size_t buflen,
127                          int ifd)
128 {
129     ssize_t ret;
130     char extrabuf[200];
131     char *p;
132     size_t l;
133 
134     memcpy(extrabuf, buf, buflen);
135 
136     /* Read a bit more context, and find the printable prefix. */
137     ret = read(ifd, extrabuf + buflen, sizeof extrabuf - 1 - buflen);
138     if (ret == -1) {
139         ret = 0;                /* pah, use what we've got */
140     }
141 
142     l = buflen + ret;
143 
144     extrabuf[l] = '\0';
145     for (p = extrabuf; *p; p++)
146         if (!(isprint((uint8_t)*p) || *p == ' ' || *p == '\t')) {
147             *p = '\0';
148             break;
149         }
150 
151     rs_log_error("error context: \"%s\"", extrabuf);
152 
153     return 0;                   /* i just feel really sad... */
154 }
155 
156 
157 /**
158  * Read a token and value.  The receiver always knows what token name
159  * is expected next -- indeed the names are really only there as a
160  * sanity check and to aid debugging.
161  *
162  * @param ifd      fd to read from
163  * @param expected 4-char token that is expected to come in next
164  * @param val      receives the parameter value
165  **/
dcc_r_token_int(int ifd,const char * expected,unsigned * val)166 int dcc_r_token_int(int ifd, const char *expected, unsigned *val)
167 {
168     char buf[13], *bum;
169     int ret;
170 
171     if (strlen(expected) != 4) {
172         rs_log_error("expected token \"%s\" seems wrong", expected);
173         return EXIT_PROTOCOL_ERROR;
174     }
175 
176     if ((ret = dcc_readx(ifd, buf, 12))) {
177         rs_log_error("read failed while waiting for token \"%s\"",
178                     expected);
179         return ret;
180     }
181 
182     if (memcmp(buf, expected, 4)) {
183         rs_log_error("protocol derailment: expected token \"%s\"", expected);
184         dcc_explain_mismatch(buf, 12, ifd);
185         return EXIT_PROTOCOL_ERROR;
186     }
187 
188     buf[12] = '\0';             /* terminate */
189 
190     *val = strtoul(&buf[4], &bum, 16);
191     if (bum != &buf[12]) {
192         rs_log_error("failed to parse parameter of token \"%s\"",
193                      expected);
194         dcc_explain_mismatch(buf, 12, ifd);
195         return EXIT_PROTOCOL_ERROR;
196     }
197 
198     rs_trace("got %s", buf);
199 
200     return 0;
201 }
202 
203 /**
204  * Read a token and value.  Fill in both token and value;
205  * unlike dcc_r_token_int this is for the case when we do not know what
206  * the next token will be.
207  *
208  * @param ifd      fd to read from
209  * @param token    receives the 4-char token
210  * @param val      receives the parameter value
211  **/
dcc_r_sometoken_int(int ifd,char * token,unsigned * val)212 int dcc_r_sometoken_int(int ifd, char *token, unsigned *val)
213 {
214     char buf[13], *bum;
215     int ret;
216 
217     if ((ret = dcc_readx(ifd, buf, 12))) {
218         rs_log_error("read failed while waiting for some token");
219         return ret;
220     }
221 
222     memcpy(token, buf, 4);
223     token[4] = '\0';
224 
225     buf[12] = '\0';             /* terminate */
226 
227     *val = strtoul(&buf[4], &bum, 16);
228     if (bum != &buf[12]) {
229         rs_log_error("failed to parse parameter of token \"%s\"",
230                      token);
231         dcc_explain_mismatch(buf, 12, ifd);
232         return EXIT_PROTOCOL_ERROR;
233     }
234 
235     rs_trace("got %s", buf);
236 
237     return 0;
238 }
239 
240 /**
241  * Read a byte string of length @p l into a newly allocated buffer, returned in @p buf.
242  **/
dcc_r_str_alloc(int fd,unsigned l,char ** buf)243 int dcc_r_str_alloc(int fd, unsigned l, char **buf)
244 {
245      char *s;
246 
247 #if 0
248      /* never true  */
249      if (l < 0) {
250          rs_log_crit("oops, l < 0");
251          return EXIT_PROTOCOL_ERROR;
252      }
253 #endif
254 
255 /*      rs_trace("read %d byte string", l); */
256 
257      s = *buf = malloc((size_t) l + 1);
258      if (!s)
259           rs_log_error("malloc failed");
260      if (dcc_readx(fd, s, (size_t) l))
261           return EXIT_OUT_OF_MEMORY;
262 
263      s[l] = 0;
264 
265      return 0;
266 }
267 
268 
269 /**
270  * Write a token, and then the string @p buf.
271  *
272  * The length of buf is determined by its nul delimiter, but the \0 is not sent.
273  **/
dcc_x_token_string(int fd,const char * token,const char * buf)274 int dcc_x_token_string(int fd,
275                        const char *token,
276                        const char *buf)
277 {
278     int ret;
279     size_t len;
280 
281     len = strlen(buf);
282     if ((ret = dcc_x_token_int(fd, token, (unsigned) len)))
283         return ret;
284     if ((ret = dcc_writex(fd, buf, len)))
285         return ret;
286     rs_trace("send string '%s'", buf);
287     return 0;
288 }
289 
290 
dcc_r_token_string(int ifd,const char * expect_token,char ** p_str)291 int dcc_r_token_string(int ifd, const char *expect_token,
292                        char **p_str)
293 {
294     unsigned a_len;
295     int ret;
296 
297     if ((ret = dcc_r_token_int(ifd, expect_token, &a_len)))
298         return ret;
299 
300     if ((ret = dcc_r_str_alloc(ifd, a_len, p_str)))
301         return ret;
302 
303     rs_trace("got '%s'", *p_str);
304 
305     return 0;
306 }
307 
308 /**
309  * Read an argv-type vector from the network.
310  **/
dcc_r_argv(int ifd,const char * argc_token,const char * argv_token,char *** argv)311 int dcc_r_argv(int ifd,
312                const char *argc_token,
313                const char *argv_token,
314                /*@out@*/ char ***argv)
315 {
316     unsigned i;
317     unsigned argc;
318     char **a;
319     int ret;
320 
321     *argv = NULL;
322 
323     if (dcc_r_token_int(ifd, argc_token, &argc))
324         return EXIT_PROTOCOL_ERROR;
325 
326     rs_trace("reading %d arguments from job submission", argc);
327 
328     /* Have to make the argv one element too long, so that it can be
329      * terminated by a null element. */
330     *argv = a = (char **) calloc((size_t) argc+1, sizeof a[0]);
331     if (a == NULL) {
332         rs_log_error("alloc failed");
333         return EXIT_OUT_OF_MEMORY;
334     }
335     a[argc] = NULL;
336 
337     for (i = 0; i < argc; i++) {
338         if ((ret = dcc_r_token_string(ifd, argv_token, &a[i])))
339             return ret;
340 
341         rs_trace("argv[%d] = \"%s\"", i, a[i]);
342     }
343 
344     dcc_trace_argv("got arguments", a);
345 
346     return 0;
347 }
348