/* * Thread sends requests back to primary node. */ static void * send_thread(void *arg) { struct hast_resource *res = arg; struct nv *nvout; struct hio *hio; void *data; size_t length; for (;;) { pjdlog_debug(2, "send: Taking request."); QUEUE_TAKE(send, hio); reqlog(LOG_DEBUG, 2, -1, hio, "send: (%p) Got request: ", hio); nvout = nv_alloc(); /* Copy sequence number. */ nv_add_uint64(nvout, hio->hio_seq, "seq"); switch (hio->hio_cmd) { case HIO_READ: if (hio->hio_error == 0) { data = hio->hio_data; length = hio->hio_length; break; } /* * We send no data in case of an error. */ /* FALLTHROUGH */ case HIO_DELETE: case HIO_FLUSH: case HIO_WRITE: data = NULL; length = 0; break; default: PJDLOG_ABORT("Unexpected command (cmd=%hhu).", hio->hio_cmd); } if (hio->hio_error != 0) nv_add_int16(nvout, hio->hio_error, "error"); if (hast_proto_send(res, res->hr_remoteout, nvout, data, length) < 0) { secondary_exit(EX_TEMPFAIL, "Unable to send reply."); } nv_free(nvout); pjdlog_debug(2, "send: (%p) Moving request to the free queue.", hio); hio_clear(hio); QUEUE_INSERT(free, hio); } /* NOTREACHED */ return (NULL); }
static bool http_get_username(struct evhttp_request *req, char *username, bool headers_sent) { const char *auth; auth = evhttp_find_header(req->input_headers, "Authorization"); if (!auth) { reqlog(req->remote_host, username, req->uri); if (!headers_sent) { evhttp_add_header(req->output_headers, "WWW-Authenticate", "Basic realm=\"pool.itzod.ru\""); evhttp_send_reply(req, 401, "not authorized", NULL); } return false; } if (!valid_auth_hdr(auth, username)) { reqlog(req->remote_host, username, req->uri); if (!headers_sent) evhttp_send_reply(req, 403, "access forbidden", NULL); return false; } return true; }
/* * Thread reads from or writes to local component and also handles DELETE and * FLUSH requests. */ static void * disk_thread(void *arg) { struct hast_resource *res = arg; struct hio *hio; ssize_t ret; bool clear_activemap, logerror; clear_activemap = true; for (;;) { pjdlog_debug(2, "disk: Taking request."); QUEUE_TAKE(disk, hio); while (clear_activemap) { unsigned char *map; size_t mapsize; /* * When first request is received, it means that primary * already received our activemap, merged it and stored * locally. We can now safely clear our activemap. */ mapsize = activemap_calc_ondisk_size(res->hr_local_mediasize - METADATA_SIZE, res->hr_extentsize, res->hr_local_sectorsize); map = calloc(1, mapsize); if (map == NULL) { pjdlog_warning("Unable to allocate memory to clear local activemap."); break; } if (pwrite(res->hr_localfd, map, mapsize, METADATA_SIZE) != (ssize_t)mapsize) { pjdlog_errno(LOG_WARNING, "Unable to store cleared activemap"); free(map); break; } free(map); clear_activemap = false; pjdlog_debug(1, "Local activemap cleared."); break; } reqlog(LOG_DEBUG, 2, -1, hio, "disk: (%p) Got request: ", hio); logerror = true; /* Handle the actual request. */ switch (hio->hio_cmd) { case HIO_READ: ret = pread(res->hr_localfd, hio->hio_data, hio->hio_length, hio->hio_offset + res->hr_localoff); if (ret < 0) hio->hio_error = errno; else if (ret != (int64_t)hio->hio_length) hio->hio_error = EIO; else hio->hio_error = 0; break; case HIO_WRITE: ret = pwrite(res->hr_localfd, hio->hio_data, hio->hio_length, hio->hio_offset + res->hr_localoff); if (ret < 0) hio->hio_error = errno; else if (ret != (int64_t)hio->hio_length) hio->hio_error = EIO; else hio->hio_error = 0; break; case HIO_DELETE: ret = g_delete(res->hr_localfd, hio->hio_offset + res->hr_localoff, hio->hio_length); if (ret < 0) hio->hio_error = errno; else hio->hio_error = 0; break; case HIO_FLUSH: if (!res->hr_localflush) { ret = -1; hio->hio_error = EOPNOTSUPP; logerror = false; break; } ret = g_flush(res->hr_localfd); if (ret < 0) { if (errno == EOPNOTSUPP) res->hr_localflush = false; hio->hio_error = errno; } else { hio->hio_error = 0; } break; default: PJDLOG_ABORT("Unexpected command (cmd=%hhu).", hio->hio_cmd); } if (logerror && hio->hio_error != 0) { reqlog(LOG_ERR, 0, hio->hio_error, hio, "Request failed: "); } pjdlog_debug(2, "disk: (%p) Moving request to the send queue.", hio); QUEUE_INSERT(send, hio); } /* NOTREACHED */ return (NULL); }
/* * Thread receives requests from the primary node. */ static void * recv_thread(void *arg) { struct hast_resource *res = arg; struct hio *hio; struct nv *nv; for (;;) { pjdlog_debug(2, "recv: Taking free request."); QUEUE_TAKE(free, hio); pjdlog_debug(2, "recv: (%p) Got request.", hio); if (hast_proto_recv_hdr(res->hr_remotein, &nv) < 0) { secondary_exit(EX_TEMPFAIL, "Unable to receive request header"); } if (requnpack(res, hio, nv) != 0) { nv_free(nv); pjdlog_debug(2, "recv: (%p) Moving request to the send queue.", hio); QUEUE_INSERT(send, hio); continue; } switch (hio->hio_cmd) { case HIO_READ: res->hr_stat_read++; break; case HIO_WRITE: res->hr_stat_write++; break; case HIO_DELETE: res->hr_stat_delete++; break; case HIO_FLUSH: res->hr_stat_flush++; break; case HIO_KEEPALIVE: break; default: PJDLOG_ABORT("Unexpected command (cmd=%hhu).", hio->hio_cmd); } reqlog(LOG_DEBUG, 2, -1, hio, "recv: (%p) Got request header: ", hio); if (hio->hio_cmd == HIO_KEEPALIVE) { nv_free(nv); pjdlog_debug(2, "recv: (%p) Moving request to the free queue.", hio); hio_clear(hio); QUEUE_INSERT(free, hio); continue; } else if (hio->hio_cmd == HIO_WRITE) { if (hast_proto_recv_data(res, res->hr_remotein, nv, hio->hio_data, MAXPHYS) < 0) { secondary_exit(EX_TEMPFAIL, "Unable to receive request data"); } } nv_free(nv); pjdlog_debug(2, "recv: (%p) Moving request to the disk queue.", hio); QUEUE_INSERT(disk, hio); } /* NOTREACHED */ return (NULL); }
static void http_handle_req(struct evhttp_request *req, lp_type longpoll, bool *req_valid) { const char *clen_str; char *body_str; char username[65] = ""; void *body, *reply = NULL; int clen = 0; unsigned int reply_len = 0; json_t *jreq; json_error_t jerr; bool rc; struct evbuffer *evbuf; if (!http_get_username(req, username, req->chunked)) return; if (longpoll == LP_NONE) { clen_str = evhttp_find_header(req->input_headers, "Content-Length"); if (clen_str) clen = atoi(clen_str); if (clen < 1 || clen > 999999) { reqlog(req->remote_host, username, req->uri); goto err_out_bad_req; } if (EVBUFFER_LENGTH(req->input_buffer) != clen) goto err_out_bad_req; body = EVBUFFER_DATA(req->input_buffer); body_str = strndup(body, clen); if (!body_str) goto err_out_bad_req; } else if (longpoll == LP_REPLY) { body_str = strdup("{\"method\":\"getwork\",\"params\":[],\"id\":1}"); } else if (longpoll == LP_KEEPALIVE || longpoll == LP_CLOSE) { reply = malloc(sizeof(char) * 2); if (!reply) goto err_out_bad_req; reply_len = snprintf(reply, 2, " "); } if (!reply) { jreq = JSON_LOADS(body_str, &jerr); free(body_str); if (!jreq) goto err_out_bad_req; rc = msg_json_rpc(req, jreq, username, &reply, &reply_len); json_decref(jreq); if (!rc) goto err_out_bad_req; } evbuf = evbuffer_new(); if (!evbuf) { free(reply); goto err_out_bad_req; } if (evbuffer_add(evbuf, reply, reply_len)) { evbuffer_free(evbuf); free(reply); goto err_out_bad_req; } free(reply); /* req_valid is a pointer to the valid member of the list struct * containing the LP request. When the connection drops and * http_lp_close_cb is called, this bool is set to false. Because we * have the reference, we can check before each send command if the * close callback has been called or if the request is still OK. * * We only have to check this when sending chunked, because if we send * in one go, there is nothing that could have closed the request, as * we're single threaded. For now. */ if (longpoll == LP_NONE) { /* Send normal requests not chunked */ evhttp_send_reply(req, HTTP_OK, "ok", evbuf); } else { if (!req->chunked && is_valid(req_valid)) evhttp_send_reply_start(req, HTTP_OK, "ok"); if (is_valid(req_valid)) evhttp_send_reply_chunk(req, evbuf); if (longpoll != LP_KEEPALIVE && is_valid(req_valid)) evhttp_send_reply_end(req); } evbuffer_free(evbuf); return; err_out_bad_req: /* When we've already sent headers, we can't really give an error so * we just send an empty reply... */ if (req->chunked) { if (is_valid(req_valid)) evhttp_send_reply_end(req); } else { evhttp_send_reply(req, HTTP_BADREQUEST, "invalid args", NULL); } }
/* * Thread receives requests from the primary node. */ static void * recv_thread(void *arg) { struct hast_resource *res = arg; struct hio *hio, *mshio; struct nv *nv; for (;;) { pjdlog_debug(2, "recv: Taking free request."); QUEUE_TAKE(free, hio); pjdlog_debug(2, "recv: (%p) Got request.", hio); if (hast_proto_recv_hdr(res->hr_remotein, &nv) == -1) { secondary_exit(EX_TEMPFAIL, "Unable to receive request header"); } if (requnpack(res, hio, nv) != 0) { nv_free(nv); pjdlog_debug(2, "recv: (%p) Moving request to the send queue.", hio); QUEUE_INSERT(send, hio); continue; } switch (hio->hio_cmd) { case HIO_READ: res->hr_stat_read++; break; case HIO_WRITE: res->hr_stat_write++; break; case HIO_DELETE: res->hr_stat_delete++; break; case HIO_FLUSH: res->hr_stat_flush++; break; case HIO_KEEPALIVE: break; default: PJDLOG_ABORT("Unexpected command (cmd=%hhu).", hio->hio_cmd); } reqlog(LOG_DEBUG, 2, -1, hio, "recv: (%p) Got request header: ", hio); if (hio->hio_cmd == HIO_KEEPALIVE) { nv_free(nv); pjdlog_debug(2, "recv: (%p) Moving request to the free queue.", hio); hio_clear(hio); QUEUE_INSERT(free, hio); continue; } else if (hio->hio_cmd == HIO_WRITE) { if (hast_proto_recv_data(res, res->hr_remotein, nv, hio->hio_data, MAXPHYS) == -1) { secondary_exit(EX_TEMPFAIL, "Unable to receive request data"); } if (hio->hio_memsync) { /* * For memsync requests we expect two replies. * Clone the hio so we can handle both of them. */ pjdlog_debug(2, "recv: Taking free request."); QUEUE_TAKE(free, mshio); pjdlog_debug(2, "recv: (%p) Got request.", mshio); hio_copy(hio, mshio); mshio->hio_error = 0; /* * We want to keep 'memsync' tag only on the * request going onto send queue (mshio). */ hio->hio_memsync = false; pjdlog_debug(2, "recv: (%p) Moving memsync request to the send queue.", mshio); QUEUE_INSERT(send, mshio); } } nv_free(nv); pjdlog_debug(2, "recv: (%p) Moving request to the disk queue.", hio); QUEUE_INSERT(disk, hio); } /* NOTREACHED */ return (NULL); }