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