int io_shuffle_sendfile(request * req) { off_t sendfile_offset; off_t bytes_written; off_t bytes_to_write; if (req->method == M_HEAD) { return complete_response(req); } /* XXX trouble if range is exactly 4G on a 32-bit machine? */ bytes_to_write = (req->ranges->stop - req->ranges->start) + 1; if (bytes_to_write > system_bufsize) bytes_to_write = system_bufsize; retrysendfile: if (bytes_to_write == 0) { /* shouldn't get here, but... */ bytes_written = 0; } else { /* arg 3 of sendfile should have type "off_t *" * struct range element start has type "unsigned long" * Where POSIX got the idea that an offset into a file * should be signed, I'll never know. */ sendfile_offset = req->ranges->start; if (sendfile_offset < 0) { req->status = DEAD; log_error_doc(req); fprintf(stderr, "impossible offset (%lu) requested of sendfile\n", req->ranges->start); return 0; } bytes_written = sendfile(req->fd, req->data_fd, &sendfile_offset, bytes_to_write); if (sendfile_offset < 0) { req->status = DEAD; log_error_doc(req); fprintf(stderr, "bad craziness in sendfile offset, returned %ld\n", (long) sendfile_offset); return 0; } req->ranges->start = sendfile_offset; if (bytes_written < 0) { if (errno == ENOSYS) { return io_shuffle(req); } else if (errno == EWOULDBLOCK || errno == EAGAIN) { /* request blocked at the pipe level, but keep going */ return -1; } else if (errno == EINTR) { goto retrysendfile; } else { req->status = DEAD; #ifdef QUIET_DISCONNECT if (0) #else if (errno != EPIPE && errno != ECONNRESET) #endif { log_error_doc(req); perror("sendfile write"); } } return 0; } else if (bytes_written == 0) { /* not sure how to handle this. * For now, treat it like it is blocked. */ return -1; }/* bytes_written */ } /* bytes_to_write */ /* sendfile automatically updates req->ranges->start * don't touch! * req->ranges->start += bytes_written; */ req->bytes_written += bytes_written; if (req->ranges->stop + 1 <= req->ranges->start) { return complete_response(req); } return 1; }
void process_requests(int server_sock) { int retval = 0; request *current, *trailer; if (pending_requests) { get_request(server_sock); #ifdef ORIGINAL_BEHAVIOR pending_requests = 0; #endif } current = request_ready; while (current) { time(¤t_time); retval = 1; /* emulate "success" in case we don't have to flush */ if (current->buffer_end && /* there is data in the buffer */ current->status < TIMED_OUT) { retval = req_flush(current); /* * retval can be -2=error, -1=blocked, or bytes left */ if (retval == -2) { /* error */ current->status = DEAD; retval = 0; } else if (retval >= 0) { /* notice the >= which is different from below? Here, we may just be flushing headers. We don't want to return 0 because we are not DONE or DEAD */ retval = 1; } } if (retval == 1) { switch (current->status) { case READ_HEADER: case ONE_CR: case ONE_LF: case TWO_CR: retval = read_header(current); break; case BODY_READ: retval = read_body(current); break; case BODY_WRITE: retval = write_body(current); break; case WRITE: retval = process_get(current); break; case PIPE_READ: retval = read_from_pipe(current); break; case PIPE_WRITE: retval = write_from_pipe(current); break; case IOSHUFFLE: #ifdef HAVE_SENDFILE retval = io_shuffle_sendfile(current); #else retval = io_shuffle(current); #endif break; case DONE: /* a non-status that will terminate the request */ retval = req_flush(current); /* * retval can be -2=error, -1=blocked, or bytes left */ if (retval == -2) { /* error */ current->status = DEAD; retval = 0; } else if (retval > 0) { retval = 1; } break; case TIMED_OUT: case DEAD: retval = 0; current->buffer_end = 0; SQUASH_KA(current); break; default: retval = 0; fprintf(stderr, "Unknown status (%d), " "closing!\n", current->status); current->status = DEAD; break; } } if (sigterm_flag) { SQUASH_KA(current); } /* we put this here instead of after the switch so that * if we are on the last request, and get_request is successful, * current->next is valid! */ if (pending_requests) get_request(server_sock); switch (retval) { case -1: /* request blocked */ trailer = current; current = current->next; block_request(trailer); break; case 0: /* request complete */ current->time_last = current_time; trailer = current; current = current->next; free_request(trailer); break; case 1: /* more to do */ current->time_last = current_time; current = current->next; break; default: log_error_doc(current); fprintf(stderr, "Unknown retval in process.c - " "Status: %d, retval: %d\n", current->status, retval); current->status = DEAD; current = current->next; break; } } }