#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/time.h>
#include "distcc.h"
#include "trace.h"
#include "io.h"
#include "rpc.h"
#include "bulk.h"
#include "time.h"
#include "timeval.h"
#ifndef O_BINARY
# define O_BINARY 0
#endif
int dcc_open_read(const char *fname, int *ifd, off_t *fsize)
{
struct stat buf;
*ifd = open(fname, O_RDONLY|O_BINARY);
if (*ifd == -1) {
int save_errno = errno;
if (save_errno == ENOENT) {
*fsize = 0;
return 0;
} else {
rs_log_error("failed to open %s: %s", fname, strerror(save_errno));
return -1;
}
}
if (fstat(*ifd, &buf) == -1) {
rs_log_error("fstat %s failed: %s", fname, strerror(errno));
close(*ifd);
return -1;
}
*fsize = buf.st_size;
return 0;
}
static void dcc_calc_rate(size_t size_out,
struct timeval *before,
struct timeval *after,
double *secs,
double *rate)
{
struct timeval delta;
timeval_subtract(&delta, after, before);
*secs = (double) delta.tv_sec + (double) delta.tv_usec / 1e6;
*rate = ((double) size_out / *secs) / 1024.0;
}
int dcc_x_file_timed(int ofd, const char *fname, const char *token,
size_t *size_out)
{
struct timeval before, after;
int ret;
size_t size;
if (gettimeofday(&before, NULL))
rs_log_warning("gettimeofday failed");
ret = dcc_x_file(ofd, fname, token, &size);
if (size_out)
*size_out = size;
if (gettimeofday(&after, NULL)) {
rs_log_warning("gettimeofday failed");
} else {
double secs, rate;
dcc_calc_rate(size, &before, &after, &secs, &rate);
rs_log_info("%ld bytes sent in %.3fs, rate %.0fkB/s",
(long) size, secs, rate);
}
return ret;
}
int dcc_x_file(int ofd, const char *fname, const char *token,
size_t *size_out)
{
int ifd;
off_t f_size;
if (dcc_open_read(fname, &ifd, &f_size))
return -1;
rs_trace("send %ld byte file %s with token %s",
(long) f_size, fname, token);
if (size_out)
*size_out = (size_t) f_size;
if (dcc_x_token_int(ofd, token, f_size))
goto failed;
if (f_size) {
#ifdef HAVE_SENDFILE
if (dcc_pump_sendfile(ofd, ifd, (size_t) f_size))
goto failed;
#else
if (dcc_pump_readwrite(ofd, ifd, (size_t) f_size))
goto failed;
#endif
}
if (ifd != -1)
close(ifd);
return 0;
failed:
if (ifd != -1)
close(ifd);
return -1;
}
int dcc_r_file(int ifd, const char *filename, size_t len)
{
int ofd;
int ret, close_ret;
if (unlink(filename) && errno != ENOENT) {
rs_log_error("failed to remove %s: %s", filename, strerror(errno));
return -1;
}
ofd = open(filename, O_TRUNC|O_WRONLY|O_CREAT|O_EXCL|O_BINARY, 0666);
if (ofd == -1) {
rs_log_error("failed to create %s: %s", filename, strerror(errno));
return -1;
}
ret = dcc_r_file_body(ofd, ifd, len);
close_ret = dcc_close(ofd);
if (!ret && !close_ret) {
rs_log_info("received %d bytes to file %s", len, filename);
return 0;
}
rs_trace("failed to receive %s, removing it", filename);
if (unlink(filename)) {
rs_log_error("failed to unlink %s after failed transfer: %s",
filename, strerror(errno));
}
return -1;
}
int dcc_r_file_body(int ofd, int ifd, size_t len)
{
return dcc_pump_readwrite(ofd, ifd, (size_t) len);
}
int dcc_r_file_timed(int ifd, const char *fname, size_t size)
{
struct timeval before, after;
int ret;
if (gettimeofday(&before, NULL))
rs_log_warning("gettimeofday failed");
ret = dcc_r_file(ifd, fname, size);
if (gettimeofday(&after, NULL)) {
rs_log_warning("gettimeofday failed");
} else {
double secs, rate;
dcc_calc_rate(size, &before, &after, &secs, &rate);
rs_log_info("%ld bytes received in %.3fs, rate %.0fkB/s",
(long) size, secs, rate);
}
return ret;
}