/** * Write bytes to an fd. Keep writing until we're all done or something goes * wrong. * * @returns 0 or exit code. **/ int dcc_writex(int fd, const void *buf, size_t len) { ssize_t r; int ret; while (len > 0) { r = write(fd, buf, len); if (r == -1 && errno == EAGAIN) { if ((ret = dcc_select_for_write(fd, dcc_io_timeout))) return ret; else continue; } else if (r == -1 && errno == EINTR) { continue; } else if (r == -1) { rs_log_error("failed to write: %s", strerror(errno)); return EXIT_IO_ERROR; } else if (r == 0) { rs_log_error("unexpected eof on fd%d", fd); return EXIT_TRUNCATED; } else { buf = &((char *) buf)[r]; len -= r; } } return 0; }
/* * Transmit the body of a file using sendfile(). * * Linux at the moment requires the input be page-based -- ie a disk file, and * only on particular filesystems. If the sendfile() call fails in a way that * makes us think that regular IO might work, then we try that instead. For * example, the /tmp filesystem may not support sendfile(). */ int dcc_pump_sendfile(int ofd, int ifd, size_t size) { ssize_t sent; off_t offset = 0; int ret; while (size) { /* Handle possibility of partial transmission, e.g. if * sendfile() is interrupted by a signal. size is decremented * as we go. */ sent = sys_sendfile(ofd, ifd, &offset, size); if (sent == -1) { if ((errno == ENOSYS || errno == EINVAL) && offset == 0) { /* The offset==0 tests is because we may be part way through * the file. We can't just naively go back to read/write * because sendfile() does not update the file pointer: we * would need to lseek() first. That case is not handled at * the moment because it's unlikely that sendfile() would * suddenly be unsupported while we're using it. A failure * halfway through probably indicates a genuine error.*/ rs_log_info("decided to use read/write rather than sendfile"); return dcc_pump_readwrite(ofd, ifd, size); } else if (errno == EAGAIN) { /* Sleep until we're able to write out more data. */ if ((ret = dcc_select_for_write(ofd, dcc_io_timeout)) != 0) return ret; rs_trace("select() returned, continuing to write"); } else if (errno == EINTR) { rs_trace("sendfile() interrupted, continuing"); } else { rs_log_error("sendfile failed: %s", strerror(errno)); return EXIT_IO_ERROR; } } else if (sent == 0) { rs_log_error("sendfile returned 0? can't cope"); return EXIT_IO_ERROR; } else if (sent != (ssize_t) size) { /* offset is automatically updated by sendfile. */ size -= sent; rs_log_notice("sendfile: partial transmission of %ld bytes; retrying %ld @%ld", (long) sent, (long) size, (long) offset); } else { /* normal case, everything was sent. */ break; } } return 0; }
/* * Connect to a host given its binary address, with a timeout. * * host and port are only here to aid printing debug messages. */ static int dcc_connect_by_addr(struct sockaddr *sa, size_t salen, int *p_fd) { int fd; int ret; char *s; int failed; dcc_sockaddr_to_string(sa, salen, &s); rs_trace("started connecting to %s", s); if ((fd = socket(sa->sa_family, SOCK_STREAM, 0)) == -1) { rs_log_error("failed to create socket: %s", strerror(errno)); ret = EXIT_CONNECT_FAILED; goto out_failed; } dcc_set_nonblocking(fd); /* start the nonblocking connect... */ do failed = connect(fd, sa, salen); while (failed == -1 && errno == EINTR); if (failed == -1 && errno != EINPROGRESS) { rs_log(RS_LOG_ERR|RS_LOG_NONAME, "failed to connect to %s: %s", s, strerror(errno)); ret = EXIT_CONNECT_FAILED; goto out_failed; } if ((ret = dcc_select_for_write(fd, dcc_connect_timeout))) { rs_log(RS_LOG_ERR|RS_LOG_NONAME, "timeout while connecting to %s", s); goto out_failed; } *p_fd = fd; free(s); return 0; out_failed: free(s); return ret; }
/* * Connect to a host given its binary address, with a timeout. * * host and port are only here to aid printing debug messages. */ int dcc_connect_by_addr(struct sockaddr *sa, size_t salen, int *p_fd) { int fd; int ret; char *s; int failed; int connecterr; int tries = 3; dcc_sockaddr_to_string(sa, salen, &s); if (s == NULL) return EXIT_OUT_OF_MEMORY; rs_trace("started connecting to %s", s); if ((fd = socket(sa->sa_family, SOCK_STREAM, 0)) == -1) { rs_log_error("failed to create socket: %s", strerror(errno)); ret = EXIT_CONNECT_FAILED; goto out_failed; } dcc_set_nonblocking(fd); /* start the nonblocking connect... */ do failed = connect(fd, sa, salen); while (failed == -1 && (errno == EINTR || (errno == EAGAIN && tries-- && poll(NULL, 0, 500) == 0))); if (failed == -1 && errno != EINPROGRESS) { rs_log(RS_LOG_ERR|RS_LOG_NONAME, "failed to connect to %s: %s", s, strerror(errno)); ret = EXIT_CONNECT_FAILED; goto out_failed; } do { socklen_t len; if ((ret = dcc_select_for_write(fd, dcc_connect_timeout))) { rs_log(RS_LOG_ERR|RS_LOG_NONAME, "timeout while connecting to %s", s); goto out_failed; } connecterr = -1; len = sizeof(connecterr); if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (char *)&connecterr, &len) < 0) { rs_log_error("getsockopt SO_ERROR failed?!"); ret = EXIT_CONNECT_FAILED; goto out_failed; } /* looping is unlikely, but I believe I needed this in dkftpbench */ /* fixme: should reduce timeout on each time around this loop */ } while (connecterr == EINPROGRESS); if (connecterr) { rs_log(RS_LOG_ERR|RS_LOG_NONAME, "nonblocking connect to %s failed: %s", s, strerror(connecterr)); ret = EXIT_CONNECT_FAILED; goto out_failed; } *p_fd = fd; free(s); return 0; out_failed: free(s); return ret; }