Esempio n. 1
0
File: pipe.c Progetto: gpg/boa
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;
}
Esempio n. 2
0
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(&current_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;
        }
    }
}