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