Ejemplo n.º 1
0
F_NONNULL
static void mon_read_cb(struct ev_loop* loop, struct ev_io* io, const int revents V_UNUSED) {
    dmn_assert(loop); dmn_assert(io);
    dmn_assert(revents == EV_READ);

    http_events_t* md = (http_events_t*)io->data;

    dmn_assert(md);
    dmn_assert(md->hstate == HTTP_STATE_READING);
    dmn_assert(ev_is_active(md->read_watcher));
    dmn_assert(!ev_is_active(md->write_watcher));
    dmn_assert(md->sock > -1);

    bool final_status = false;
    const int to_recv = 13 - md->done;
    const int recvd = recv(md->sock, md->res_buf + md->done, to_recv, 0);
    if(unlikely(recvd == -1)) {
        switch(errno) {
            case EAGAIN:
            case EINTR:
                return;
            case ETIMEDOUT:
            case ENOTCONN:
            case ECONNRESET:
            case EPIPE:
                break;
            default:
                log_err("plugin_http_status: read() from monitoring socket failed, possible local problem: %s", logf_errno());
        }
    }
    else if(recvd < to_recv) {
        md->done += recvd;
        return;
    }
    else {
        md->res_buf[13] = '\0';
        char code_str[4] = { 0 };
        if(1 == sscanf(md->res_buf, "HTTP/1.%*1[01]%*1[ ]%3c%*1[ ]", code_str)) {
            unsigned long lcode = strtoul(code_str, NULL, 10);
            for(unsigned i = 0; i < md->http_svc->num_ok_codes; i++) {
                if(lcode == md->http_svc->ok_codes[i]) {
                    final_status = true;
                    break;
                }
            }
        }
    }

    // I don't believe we actually need to read the rest of the response before
    //   shutdown/close in order to avoid bad TCP behavior, but I could be wrong.

    log_debug("plugin_http_status: State poll of %s %s", md->smgr->desc, final_status ? "succeeded" : "failed");
    shutdown(md->sock, SHUT_RDWR);
    close(md->sock);
    md->sock = -1;
    ev_io_stop(loop, md->read_watcher);
    ev_timer_stop(loop, md->timeout_watcher);
    md->hstate = HTTP_STATE_WAITING;
    gdnsd_mon_state_updater(md->smgr, final_status);
}
Ejemplo n.º 2
0
F_NONNULL
static void mon_connect_cb(struct ev_loop* loop, struct ev_io* io, const int revents V_UNUSED) {
    dmn_assert(loop); dmn_assert(io);
    dmn_assert(revents == EV_WRITE);

    tcp_events_t* md = io->data;

    dmn_assert(md);
    dmn_assert(md->tcp_state == TCP_STATE_CONNECTING);
    dmn_assert(ev_is_active(md->connect_watcher));
    dmn_assert(ev_is_active(md->timeout_watcher) || ev_is_pending(md->timeout_watcher));
    dmn_assert(md->sock > -1);

    // nonblocking connect() just finished, need to check status
    bool success = false;
    int sock = md->sock;
    int so_error = 0;
    unsigned so_error_len = sizeof(so_error);
    (void)getsockopt(sock, SOL_SOCKET, SO_ERROR, &so_error, &so_error_len);
    if(unlikely(so_error)) {
        switch(so_error) {
            case EPIPE:
            case ECONNREFUSED:
            case ETIMEDOUT:
            case EHOSTUNREACH:
            case EHOSTDOWN:
            case ENETUNREACH:
                log_debug("plugin_tcp_connect: State poll of %s failed quickly: %s", md->desc, dmn_logf_strerror(so_error));
                break;
            default:
                log_err("plugin_tcp_connect: Failed to connect() monitoring socket to remote server, possible local problem: %s", dmn_logf_strerror(so_error));
        }
    }
    else {
        success = true;
    }

    shutdown(sock, SHUT_RDWR);
    close(sock);
    md->sock = -1;
    ev_io_stop(loop, md->connect_watcher);
    ev_timer_stop(loop, md->timeout_watcher);
    md->tcp_state = TCP_STATE_WAITING;
    gdnsd_mon_state_updater(md->idx, success);
}
Ejemplo n.º 3
0
F_NONNULL
static void mon_timeout_cb(struct ev_loop* loop, struct ev_timer* t, const int revents V_UNUSED) {
    dmn_assert(loop); dmn_assert(t);
    dmn_assert(revents == EV_TIMER);

    tcp_events_t* md = t->data;

    dmn_assert(md);
    dmn_assert(md->sock > -1);
    dmn_assert(md->tcp_state == TCP_STATE_CONNECTING);
    dmn_assert(ev_is_active(md->connect_watcher));

    log_debug("plugin_tcp_connect: State poll of %s timed out", md->desc);
    ev_io_stop(loop, md->connect_watcher);
    shutdown(md->sock, SHUT_RDWR);
    close(md->sock);
    md->sock = -1;
    md->tcp_state = TCP_STATE_WAITING;
    gdnsd_mon_state_updater(md->idx, false);
}
Ejemplo n.º 4
0
F_NONNULL
static void mon_timeout_cb(struct ev_loop* loop, struct ev_timer* t, const int revents V_UNUSED) {
    dmn_assert(loop); dmn_assert(t);
    dmn_assert(revents == EV_TIMER);

    http_events_t* md = (http_events_t*)t->data;

    dmn_assert(md);
    dmn_assert(md->sock != -1);
    dmn_assert(
        (md->hstate == HTTP_STATE_READING && ev_is_active(md->read_watcher))
     || (md->hstate == HTTP_STATE_WRITING && ev_is_active(md->write_watcher))
    );

    log_debug("plugin_http_status: State poll of %s timed out", md->smgr->desc);
    if(md->hstate == HTTP_STATE_READING) ev_io_stop(loop, md->read_watcher);
    else if(md->hstate == HTTP_STATE_WRITING) ev_io_stop(loop, md->write_watcher);
    shutdown(md->sock, SHUT_RDWR);
    close(md->sock);
    md->sock = -1;
    md->hstate = HTTP_STATE_WAITING;
    gdnsd_mon_state_updater(md->smgr, false);
}
Ejemplo n.º 5
0
F_NONNULL
static void mon_interval_cb(struct ev_loop* loop, struct ev_timer* t, const int revents V_UNUSED) {
    dmn_assert(loop); dmn_assert(t);
    dmn_assert(revents == EV_TIMER);

    tcp_events_t* md = t->data;

    dmn_assert(md);

    if(md->tcp_state != TCP_STATE_WAITING) {
        log_warn("plugin_tcp_connect: A monitoring request attempt seems to have "
            "lasted longer than the monitoring interval. "
            "Skipping this round of monitoring - are you "
            "starved for CPU time?");
        return;
    }

    dmn_assert(md->sock == -1);
    dmn_assert(!ev_is_active(md->connect_watcher));
    dmn_assert(!ev_is_active(md->timeout_watcher) && !ev_is_pending(md->timeout_watcher));

    log_debug("plugin_tcp_connect: Starting state poll of %s", md->desc);

    const bool isv6 = md->addr.sa.sa_family == AF_INET6;

    const int sock = socket(isv6 ? PF_INET6 : PF_INET, SOCK_STREAM, gdnsd_getproto_tcp());
    if(sock == -1) {
        log_err("plugin_tcp_connect: Failed to create monitoring socket: %s", dmn_logf_errno());
        return;
    }

    if(fcntl(sock, F_SETFL, (fcntl(sock, F_GETFL, 0)) | O_NONBLOCK) == -1) {
        log_err("plugin_tcp_connect: Failed to set O_NONBLOCK on monitoring socket: %s", dmn_logf_errno());
        close(sock);
        return;
    }

    bool success = false;
    if(likely(connect(sock, &md->addr.sa, md->addr.len) == -1)) {
        switch(errno) {
            case EINPROGRESS:
                // this is the normal case, where nonblock connect
                //   wants us to wait for writability...
                md->sock = sock;
                md->tcp_state = TCP_STATE_CONNECTING;
                ev_io_set(md->connect_watcher, sock, EV_WRITE);
                ev_io_start(loop, md->connect_watcher);
                ev_timer_set(md->timeout_watcher, md->tcp_svc->timeout, 0);
                ev_timer_start(loop, md->timeout_watcher);
                return; // don't do socket/status finishing actions below...
                break; // redundant
            case EPIPE:
            case ECONNREFUSED:
            case ETIMEDOUT:
            case EHOSTUNREACH:
            case EHOSTDOWN:
            case ENETUNREACH:
                // fast remote failures, e.g. when remote is local, I hope
                log_debug("plugin_tcp_connect: State poll of %s failed very quickly", md->desc);
                break;
            default:
                log_err("plugin_tcp_connect: Failed to connect() monitoring socket to remote server, possible local problem: %s", dmn_logf_errno());
        }
    }
    else {
        success = true;
    }

    close(sock);
    gdnsd_mon_state_updater(md->idx, success);
}
Ejemplo n.º 6
0
F_NONNULL
static void mon_interval_cb(struct ev_loop* loop, struct ev_timer* t, const int revents V_UNUSED) {
    dmn_assert(loop); dmn_assert(t);
    dmn_assert(revents == EV_TIMER);

    http_events_t* md = (http_events_t*)t->data;

    dmn_assert(md);

    if(unlikely(md->hstate != HTTP_STATE_WAITING)) {
        log_warn("plugin_http_status: A monitoring request attempt seems to have "
            "lasted longer than the monitoring interval. "
            "Skipping this round of monitoring - are you "
            "starved for CPU time?");
        return;
    }

    dmn_assert(md->sock == -1);
    dmn_assert(!ev_is_active(md->read_watcher));
    dmn_assert(!ev_is_active(md->write_watcher));
    dmn_assert(!ev_is_active(md->timeout_watcher));

    log_debug("plugin_http_status: Starting state poll of %s", md->smgr->desc);

    do {
        const bool isv6 = md->addr.sa.sa_family == AF_INET6;

        const int sock = socket(isv6 ? PF_INET6 : PF_INET, SOCK_STREAM, gdnsd_getproto_tcp());
        if(unlikely(sock < 0)) {
            log_err("plugin_http_status: Failed to create monitoring socket: %s", logf_errno());
            break;
        }

        if(unlikely(fcntl(sock, F_SETFL, (fcntl(sock, F_GETFL, 0)) | O_NONBLOCK) == -1)) {
            log_err("plugin_http_status: Failed to set O_NONBLOCK on monitoring socket: %s", logf_errno());
            close(sock);
            break;
        }

        md->already_connected = true;
        if(likely(connect(sock, &md->addr.sa, md->addr.len) == -1)) {
            if(likely(errno == EINPROGRESS)) { md->already_connected = false; }
            else {
                switch(errno) {
                    case EPIPE:
                    case ECONNREFUSED:
                    case ETIMEDOUT:
                    case EHOSTUNREACH:
                    case EHOSTDOWN:
                    case ENETUNREACH:
                        break;
                    default:
                        log_err("plugin_http_status: Failed to connect() monitoring socket to remote server, possible local problem: %s", logf_errno());
                }
                close(sock);
                break;
            }
        }

        md->sock = sock;
        md->hstate = HTTP_STATE_WRITING;
        md->done = 0;
        ev_io_set(md->write_watcher, sock, EV_WRITE);
        ev_io_start(loop, md->write_watcher);
        ev_timer_set(md->timeout_watcher, md->http_svc->timeout, 0);
        ev_timer_start(loop, md->timeout_watcher);
        return;
    } while(0);

    // This is only reachable via "break"'s above, which indicate an immediate failure
    log_debug("plugin_http_status: State poll of %s failed very quickly", md->smgr->desc);
    md->hstate = HTTP_STATE_WAITING;
    gdnsd_mon_state_updater(md->smgr, false);
}
Ejemplo n.º 7
0
F_NONNULL
static void mon_write_cb(struct ev_loop* loop, struct ev_io* io, const int revents V_UNUSED) {
    dmn_assert(loop); dmn_assert(io);
    dmn_assert(revents == EV_WRITE);

    http_events_t* md = (http_events_t*)io->data;

    dmn_assert(md);
    dmn_assert(md->hstate == HTTP_STATE_WRITING);
    dmn_assert(!ev_is_active(md->read_watcher));
    dmn_assert(ev_is_active(md->write_watcher));
    dmn_assert(ev_is_active(md->timeout_watcher));
    dmn_assert(md->sock > -1);

    int sock = md->sock;
    if(likely(!md->already_connected)) {
        // nonblocking connect() just finished, need to check status
        int so_error = 0;
        unsigned int so_error_len = sizeof(so_error);
        (void)getsockopt(sock, SOL_SOCKET, SO_ERROR, &so_error, &so_error_len);
        if(unlikely(so_error)) {
            switch(so_error) {
                case EPIPE:
                case ECONNREFUSED:
                case ETIMEDOUT:
                case EHOSTUNREACH:
                case EHOSTDOWN:
                case ENETUNREACH:
                    break;
                default:
                    log_err("plugin_http_status: Failed to connect() monitoring socket to remote server, possible local problem: %s", logf_errnum(so_error));
            }

            log_debug("plugin_http_status: State poll of %s failed quickly: %s", md->smgr->desc, logf_errnum(so_error));
            close(sock); md->sock = -1;
            ev_io_stop(loop, md->write_watcher);
            ev_timer_stop(loop, md->timeout_watcher);
            md->hstate = HTTP_STATE_WAITING;
            gdnsd_mon_state_updater(md->smgr, false);
            return;
        }
        md->already_connected = true;
    }

    const unsigned to_send = md->http_svc->req_data_len - md->done;
    const int sent = send(sock, md->http_svc->req_data + md->done, md->http_svc->req_data_len, 0);
    if(unlikely(sent == -1)) {
        switch(errno) {
            case EAGAIN:
            case EINTR:
                return;
            case ENOTCONN:
            case ECONNRESET:
            case ETIMEDOUT:
            case EHOSTUNREACH:
            case ENETUNREACH:
            case EPIPE:
                break;
            default:
                log_err("plugin_http_status: write() to monitoring socket failed, possible local problem: %s", logf_errno());
        }
        shutdown(sock, SHUT_RDWR);
        close(sock);
        md->sock = -1;
        ev_io_stop(loop, md->write_watcher);
        ev_timer_stop(loop, md->timeout_watcher);
        md->hstate = HTTP_STATE_WAITING;
        gdnsd_mon_state_updater(md->smgr, false);
    }
    if(unlikely(sent != (signed)to_send)) {
        md->done += sent;
        return;
    }

    md->done = 0;
    md->hstate = HTTP_STATE_READING;
    ev_io_stop(loop, md->write_watcher);
    ev_io_set(md->read_watcher, sock, EV_READ);
    ev_io_start(loop, md->read_watcher);
}