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 /* "A new contraption to capture a dandelion in one
25 * piece has been put together by the crew."
26 * -- Boards of Canada, "Geogaddi" */
27
28
29 /**
30 * @file
31 *
32 * Bulk file transfer, used for sending .i, .o files etc.
33 *
34 * Files are always sent in the standard IO format: stream name,
35 * length, bytes. This implies that we can deliver to a fifo (just
36 * keep writing), but we can't send from a fifo, because we wouldn't
37 * know how many bytes were coming.
38 *
39 * @note We don't time transmission of files: because the write returns when
40 * they've just been written into the OS buffer, we don't really get
41 * meaningful numbers except for files that are very large.
42 **/
43
44 #include <config.h>
45
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <unistd.h>
49 #include <string.h>
50 #include <fcntl.h>
51 #include <errno.h>
52
53 #include <sys/stat.h>
54 #include <sys/time.h>
55
56 #include "distcc.h"
57 #include "trace.h"
58 #include "rpc.h"
59 #include "bulk.h"
60 #include "time.h"
61 #include "exitcode.h"
62 #include "timeval.h"
63
64
65 /**
66 * Open a file for read, and also put its size into @p fsize.
67 *
68 * If the file does not exist, then returns 0, but @p ifd is -1 and @p
69 * fsize is zero. If @p fsize is zero, the caller should not try to
70 * read from the file.
71 *
72 * This strange behaviour for ENOENT is useful because if there is
73 * e.g. no output file from the compiler, we don't want to abort, but
74 * rather just send nothing. The receiver has the corresponding
75 * behaviour of not creating zero-length files.
76 *
77 * Using fstat() helps avoid a race condition -- not a security issue,
78 * but possibly a failure. Shouldn't be very likely though.
79 *
80 * The caller is responsible for closing @p ifd.
81 **/
dcc_open_read(const char * fname,int * ifd,off_t * fsize)82 int dcc_open_read(const char *fname, int *ifd, off_t *fsize)
83 {
84 struct stat buf;
85
86 *ifd = open(fname, O_RDONLY|O_BINARY);
87 if (*ifd == -1) {
88 int save_errno = errno;
89 if (save_errno == ENOENT) {
90 /* that's OK, just assume it's empty */
91 *fsize = 0;
92 return 0;
93 } else {
94 rs_log_error("failed to open %s: %s", fname, strerror(save_errno));
95 return EXIT_IO_ERROR;
96 }
97 }
98
99 if (fstat(*ifd, &buf) == -1) {
100 rs_log_error("fstat %s failed: %s", fname, strerror(errno));
101 dcc_close(*ifd);
102 return EXIT_IO_ERROR;
103 }
104
105 *fsize = buf.st_size;
106
107 return 0;
108 }
109
dcc_calc_rate(off_t size_out,struct timeval * before,struct timeval * after,double * secs,double * rate)110 void dcc_calc_rate(off_t size_out,
111 struct timeval *before,
112 struct timeval *after,
113 double *secs,
114 double *rate)
115 {
116 struct timeval delta;
117
118 /* FIXME: Protect against division by zero and other floating-point
119 * exceptions. */
120
121 timeval_subtract(&delta, after, before);
122
123 *secs = (double) delta.tv_sec + (double) delta.tv_usec / 1e6;
124
125 if (*secs == 0.0)
126 *rate = 0.0;
127 else
128 *rate = ((double) size_out / *secs) / 1024.0;
129 }
130
131
dcc_x_file_lzo1x(int out_fd,int in_fd,const char * token,unsigned in_len)132 static int dcc_x_file_lzo1x(int out_fd,
133 int in_fd,
134 const char *token,
135 unsigned in_len)
136 {
137 int ret;
138 char *out_buf = NULL;
139 size_t out_len;
140
141 /* As a special case, send 0 as 0 */
142 if (in_len == 0) {
143 if ((ret = dcc_x_token_int(out_fd, token, 0)))
144 goto out;
145 } else {
146 if ((ret = dcc_compress_file_lzo1x(in_fd, in_len, &out_buf, &out_len)))
147 goto out;
148
149 if ((ret = dcc_x_token_int(out_fd, token, out_len)))
150 goto out;
151
152 if ((ret = dcc_writex(out_fd, out_buf, out_len)))
153 goto out;
154 }
155
156 ret = 0;
157
158 out:
159 free(out_buf);
160 return ret;
161 }
162
163
164 /**
165 * Transmit from a local file to the network. Sends TOKEN, LENGTH, BODY,
166 * where the length is the appropriate compressed length.
167 *
168 * Does compression if needed.
169 *
170 * @param ofd File descriptor for the network connection.
171 * @param fname Name of the file to send.
172 * @param token Token for this file, e.g. "DOTO".
173 **/
dcc_x_file(int ofd,const char * fname,const char * token,enum dcc_compress compression,off_t * f_size_out)174 int dcc_x_file(int ofd,
175 const char *fname,
176 const char *token,
177 enum dcc_compress compression,
178 off_t *f_size_out)
179 {
180 int ifd;
181 int ret;
182 off_t f_size;
183
184 if (dcc_open_read(fname, &ifd, &f_size))
185 return EXIT_IO_ERROR;
186 if (ifd == -1)
187 return EXIT_IO_ERROR;
188 if (f_size_out)
189 *f_size_out = f_size;
190
191 rs_trace("send %lu byte file %s with token %s and compression %d",
192 (unsigned long) f_size, fname, token, compression);
193
194 if (compression == DCC_COMPRESS_NONE) {
195 if ((ret = dcc_x_token_int(ofd, token, f_size)))
196 goto failed;
197
198 /* FIXME: These could get truncated if the file was very large (>4G).
199 * That seems pretty unlikely. */
200 #ifdef HAVE_SENDFILE
201 ret = dcc_pump_sendfile(ofd, ifd, (size_t) f_size);
202 #else
203 ret = dcc_pump_readwrite(ofd, ifd, (size_t) f_size);
204 #endif
205 } else if (compression == DCC_COMPRESS_LZO1X) {
206 ret = dcc_x_file_lzo1x(ofd, ifd, token, f_size);
207 } else {
208 rs_log_error("invalid compression");
209 return EXIT_PROTOCOL_ERROR;
210 }
211
212 if (ifd != -1)
213 dcc_close(ifd);
214 return 0;
215
216 failed:
217 if (ifd != -1)
218 dcc_close(ifd);
219 return ret;
220 }
221
222
223 /**
224 * Receive a file stream from the network into a local file.
225 * Make all necessary directories if they don't exist.
226 *
227 * Can handle compression.
228 *
229 * @param len Compressed length of the incoming file.
230 * @param filename local filename to create.
231 **/
dcc_r_file(int ifd,const char * filename,unsigned len,enum dcc_compress compr)232 int dcc_r_file(int ifd, const char *filename,
233 unsigned len,
234 enum dcc_compress compr)
235 {
236 int ofd;
237 int ret, close_ret;
238 struct stat s;
239
240 /* This is meant to behave similarly to the output routines in bfd/cache.c
241 * in gnu binutils, because makefiles or configure scripts may depend on
242 * it for edge cases.
243 *
244 * We try to remove the output file first, if its size is not 0. That
245 * should make the newly created file be owned by the current user; it
246 * might also help in the dangerous case of some other process still
247 * reading from the file.
248 *
249 * Checking for size 0 means that we won't unlink special files like
250 * /dev/null or fifos.
251 *
252 * However, failure to remove the file does not cause a warning; we may
253 * not have write permission on the directory, but +w for the file.
254 */
255
256 if (dcc_mk_tmp_ancestor_dirs(filename)) {
257 rs_log_error("failed to create path for '%s'", filename);
258 return EXIT_IO_ERROR;
259 }
260
261 if (stat(filename, &s) == 0) {
262 if (s.st_size != 0) {
263 if (unlink(filename) && errno != ENOENT) {
264 rs_trace("failed to remove %s: %s", filename, strerror(errno));
265 /* continue */
266 }
267 }
268 } else {
269 if (errno != ENOENT) {
270 rs_trace("stat %s failed: %s", filename, strerror(errno));
271 }
272 /* continue */
273 }
274
275 ofd = open(filename, O_TRUNC|O_WRONLY|O_CREAT|O_BINARY, 0666);
276 if (ofd == -1) {
277 rs_log_error("failed to create %s: %s", filename, strerror(errno));
278 return EXIT_IO_ERROR;
279 }
280
281 ret = 0;
282 if (len > 0) {
283 ret = dcc_r_bulk(ofd, ifd, len, compr);
284 }
285 close_ret = dcc_close(ofd);
286
287 if (!ret && !close_ret) {
288 rs_trace("received %d bytes to file %s", len, filename);
289 return 0;
290 }
291
292 rs_trace("failed to receive %s, removing it", filename);
293 if (unlink(filename)) {
294 rs_log_error("failed to unlink %s after failed transfer: %s",
295 filename, strerror(errno));
296 }
297 return EXIT_IO_ERROR;
298 }
299
300
301
302 /**
303 * Receive a file and print timing statistics. Only used for big files.
304 *
305 * Wrapper around dcc_r_file().
306 **/
dcc_r_file_timed(int ifd,const char * fname,unsigned size,enum dcc_compress compr)307 int dcc_r_file_timed(int ifd, const char *fname, unsigned size,
308 enum dcc_compress compr)
309 {
310 struct timeval before, after;
311 int ret;
312
313 if (gettimeofday(&before, NULL))
314 rs_log_warning("gettimeofday failed");
315
316 ret = dcc_r_file(ifd, fname, size, compr);
317
318 if (gettimeofday(&after, NULL)) {
319 rs_log_warning("gettimeofday failed");
320 } else {
321 double secs, rate;
322
323 dcc_calc_rate(size, &before, &after, &secs, &rate);
324 rs_log_info("%ld bytes received in %.6fs, rate %.0fkB/s",
325 (long) size, secs, rate);
326 }
327
328 return ret;
329 }
330
dcc_r_token_file(int in_fd,const char * token,const char * fname,enum dcc_compress compr)331 int dcc_r_token_file(int in_fd,
332 const char *token,
333 const char *fname,
334 enum dcc_compress compr)
335 {
336 int ret;
337 unsigned i_size;
338
339 if ((ret = dcc_r_token_int(in_fd, token, &i_size)))
340 return ret;
341
342 if ((ret = dcc_r_file_timed(in_fd, fname, (size_t) i_size, compr)))
343 return ret;
344
345 return 0;
346 }
347
dcc_copy_file_to_fd(const char * in_fname,int out_fd)348 int dcc_copy_file_to_fd(const char *in_fname, int out_fd)
349 {
350 off_t len;
351 int ifd;
352 int ret;
353
354 if ((ret = dcc_open_read(in_fname, &ifd, &len)))
355 return ret;
356
357 #ifdef HAVE_SENDFILE
358 ret = dcc_pump_sendfile(out_fd, ifd, (size_t) len);
359 #else
360 ret = dcc_pump_readwrite(out_fd, ifd, (size_t) len);
361 #endif
362
363 if (ret) {
364 close(ifd);
365 return ret;
366 }
367 return 0;
368 }
369