/* send an attention. this may get called at any time, so we can't use * DSI buffers to send one. return 0 on error */ int dsi_attention(DSI *dsi, AFPUserBytes flags) { /* header + AFPUserBytes */ char block[DSI_BLOCKSIZ + sizeof(AFPUserBytes)]; u_int32_t len, nlen; u_int16_t id; if (dsi->flags & DSI_SLEEPING) return 1; if (dsi->in_write) { return -1; } id = htons(dsi_serverID(dsi)); flags = htons(flags); len = MIN(sizeof(flags), dsi->attn_quantum); nlen = htonl(len); memset(block, 0, sizeof(block)); block[0] = DSIFL_REQUEST; /* sending a request */ block[1] = DSIFUNC_ATTN; /* it's an attention */ memcpy(block + 2, &id, sizeof(id)); /* code = 0 */ memcpy(block + 8, &nlen, sizeof(nlen)); memcpy(block + 16, &flags, sizeof(flags)); /* reserved = 0 */ /* send an attention */ return dsi_stream_write(dsi, block, DSI_BLOCKSIZ + len, DSI_NOWAIT); }
/* server generated tickles. as this is only called by the tickle handler, * we don't need to block signals. */ int dsi_tickle(DSI *dsi) { char block[DSI_BLOCKSIZ]; uint16_t id; if ((dsi->flags & DSI_SLEEPING) || dsi->in_write) return 1; id = htons(dsi_serverID(dsi)); memset(block, 0, sizeof(block)); block[0] = DSIFL_REQUEST; block[1] = DSIFUNC_TICKLE; memcpy(block + 2, &id, sizeof(id)); /* code = len = reserved = 0 */ return dsi_stream_write(dsi, block, DSI_BLOCKSIZ, DSI_NOWAIT); }
/* --------------------------------------- * write data. 0 on failure. this assumes that dsi_len will never * cause an overflow in the data buffer. */ int dsi_stream_send(DSI *dsi, void *buf, size_t length) { char block[DSI_BLOCKSIZ]; struct iovec iov[2]; int iovecs = 2; size_t towrite; ssize_t len; LOG(log_maxdebug, logtype_dsi, "dsi_stream_send(%u bytes): START", length); if (dsi->flags & DSI_DISCONNECTED) return 0; dsi_header_pack_reply(dsi, block); if (!length) { /* just write the header */ LOG(log_maxdebug, logtype_dsi, "dsi_stream_send(%u bytes): DSI header, no data", sizeof(block)); length = (dsi_stream_write(dsi, block, sizeof(block), 0) == sizeof(block)); return length; /* really 0 on failure, 1 on success */ } /* block signals */ block_sig(dsi); iov[0].iov_base = block; iov[0].iov_len = sizeof(block); iov[1].iov_base = buf; iov[1].iov_len = length; towrite = sizeof(block) + length; dsi->write_count += towrite; while (towrite > 0) { if (((len = writev(dsi->socket, iov, iovecs)) == -1 && errno == EINTR) || (len == 0)) continue; if ((size_t)len == towrite) /* wrote everything out */ break; else if (len < 0) { /* error */ if (errno == EAGAIN || errno == EWOULDBLOCK) { if (dsi_peek(dsi) == 0) { continue; } } LOG(log_error, logtype_dsi, "dsi_stream_send: %s", strerror(errno)); unblock_sig(dsi); return 0; } towrite -= len; if (towrite > length) { /* skip part of header */ iov[0].iov_base = (char *) iov[0].iov_base + len; iov[0].iov_len -= len; } else { /* skip to data */ if (iovecs == 2) { iovecs = 1; len -= iov[0].iov_len; iov[0] = iov[1]; } iov[0].iov_base = (char *) iov[0].iov_base + len; iov[0].iov_len -= len; } } LOG(log_maxdebug, logtype_dsi, "dsi_stream_send(%u bytes): END", length); unblock_sig(dsi); return 1; }
ssize_t dsi_stream_read_file(DSI *dsi, const int fromfd, off_t offset, const size_t length, const int err) { int ret = 0; size_t written = 0; size_t total = length; ssize_t len; off_t pos = offset; char block[DSI_BLOCKSIZ]; #ifdef HAVE_SENDFILEV int sfvcnt; struct sendfilevec vec[2]; ssize_t nwritten; #elif defined(FREEBSD) ssize_t nwritten; void *hdrp; struct sf_hdtr hdr; struct iovec iovec; hdr.headers = &iovec; hdr.hdr_cnt = 1; hdr.trailers = NULL; hdr.trl_cnt = 0; hdrp = &hdr; #endif LOG(log_maxdebug, logtype_dsi, "dsi_stream_read_file(off: %jd, len: %zu)", (intmax_t)offset, length); if (dsi->flags & DSI_DISCONNECTED) return -1; dsi->in_write++; dsi->flags |= DSI_NOREPLY; dsi->header.dsi_flags = DSIFL_REPLY; dsi->header.dsi_len = htonl(length); dsi->header.dsi_code = htonl(err); dsi_header_pack_reply(dsi, block); #ifdef HAVE_SENDFILEV total += DSI_BLOCKSIZ; sfvcnt = 2; vec[0].sfv_fd = SFV_FD_SELF; vec[0].sfv_flag = 0; /* Cast to unsigned long to prevent sign extension of the * pointer value for the LFS case; see Apache PR 39463. */ vec[0].sfv_off = (unsigned long)block; vec[0].sfv_len = DSI_BLOCKSIZ; vec[1].sfv_fd = fromfd; vec[1].sfv_flag = 0; vec[1].sfv_off = offset; vec[1].sfv_len = length; #elif defined(FREEBSD) iovec.iov_base = block; iovec.iov_len = DSI_BLOCKSIZ; #else dsi_stream_write(dsi, block, sizeof(block), DSI_MSG_MORE); #endif while (written < total) { #ifdef HAVE_SENDFILEV nwritten = 0; len = sendfilev(dsi->socket, vec, sfvcnt, &nwritten); #elif defined(FREEBSD) len = sendfile(fromfd, dsi->socket, pos, total - written, hdrp, &nwritten, 0); if (len == 0) len = nwritten; #else len = sys_sendfile(dsi->socket, fromfd, &pos, total - written); #endif if (len < 0) { switch (errno) { case EINTR: case EAGAIN: len = 0; #if defined(HAVE_SENDFILEV) || defined(FREEBSD) len = (size_t)nwritten; #elif defined(SOLARIS) if (pos > offset) { /* we actually have sent sth., adjust counters and keep trying */ len = pos - offset; offset = pos; } #endif /* HAVE_SENDFILEV */ if (dsi_peek(dsi) != 0) { ret = -1; goto exit; } break; default: LOG(log_error, logtype_dsi, "dsi_stream_read_file: %s", strerror(errno)); ret = -1; goto exit; } } else if (len == 0) { /* afpd is going to exit */ ret = -1; goto exit; } #ifdef HAVE_SENDFILEV if (sfvcnt == 2 && len >= vec[0].sfv_len) { vec[1].sfv_off += len - vec[0].sfv_len; vec[1].sfv_len -= len - vec[0].sfv_len; vec[0] = vec[1]; sfvcnt = 1; } else { vec[0].sfv_off += len; vec[0].sfv_len -= len; } #elif defined(FREEBSD) if (hdrp) { if (len >= iovec.iov_len) { hdrp = NULL; len -= iovec.iov_len; /* len now contains how much sendfile() actually sent from the file */ } else { iovec.iov_len -= len; iovec.iov_base += len; len = 0; } } pos += len; #endif /* HAVE_SENDFILEV */ LOG(log_maxdebug, logtype_dsi, "dsi_stream_read_file: wrote: %zd", len); written += len; } #ifdef HAVE_SENDFILEV written -= DSI_BLOCKSIZ; #endif dsi->write_count += written; exit: dsi->in_write--; LOG(log_maxdebug, logtype_dsi, "dsi_stream_read_file: written: %zd", written); if (ret != 0) return -1; return written; }
/* --------------------------------------- * write data. 0 on failure. this assumes that dsi_len will never * cause an overflow in the data buffer. */ int dsi_stream_send(DSI *dsi, void *buf, size_t length) { char block[DSI_BLOCKSIZ]; struct iovec iov[2]; size_t towrite; ssize_t len; LOG(log_maxdebug, logtype_dsi, "dsi_stream_send: %u bytes", length ? length : sizeof(block)); if (dsi->flags & DSI_DISCONNECTED) return 0; block[0] = dsi->header.dsi_flags; block[1] = dsi->header.dsi_command; memcpy(block + 2, &dsi->header.dsi_requestID, sizeof(dsi->header.dsi_requestID)); memcpy(block + 4, &dsi->header.dsi_code, sizeof(dsi->header.dsi_code)); memcpy(block + 8, &dsi->header.dsi_len, sizeof(dsi->header.dsi_len)); memcpy(block + 12, &dsi->header.dsi_reserved, sizeof(dsi->header.dsi_reserved)); if (!length) { /* just write the header */ length = (dsi_stream_write(dsi, block, sizeof(block), 0) == sizeof(block)); return length; /* really 0 on failure, 1 on success */ } /* block signals */ block_sig(dsi); iov[0].iov_base = block; iov[0].iov_len = sizeof(block); iov[1].iov_base = buf; iov[1].iov_len = length; towrite = sizeof(block) + length; dsi->write_count += towrite; while (towrite > 0) { if (((len = writev(dsi->socket, iov, 2)) == -1 && errno == EINTR) || (len == 0)) continue; if ((size_t)len == towrite) /* wrote everything out */ break; else if (len < 0) { /* error */ if (errno == EAGAIN || errno == EWOULDBLOCK) { if (!dsi_peek(dsi)) { continue; } } LOG(log_error, logtype_dsi, "dsi_stream_send: %s", strerror(errno)); unblock_sig(dsi); return 0; } towrite -= len; if (towrite > length) { /* skip part of header */ iov[0].iov_base = (char *) iov[0].iov_base + len; iov[0].iov_len -= len; } else { /* skip to data */ if (iov[0].iov_len) { len -= iov[0].iov_len; iov[0].iov_len = 0; } iov[1].iov_base = (char *) iov[1].iov_base + len; iov[1].iov_len -= len; } } unblock_sig(dsi); return 1; }