static void send_error_helper(const char sep, int errnum, const char *message) { CGI_PRINTF("%c\"ErrorMessage\":", sep); json_send_qstring(message); if (errnum == 500 || errnum == 400) { if (message) WARN("HTTP %d: %s", errnum, message); CGI_PUTS(",\"ErrorDetails\":"); json_send_qstring(msg_log_end()); } CGI_PUTS(",\"NodeId\":"); const sx_node_t *me = sx_hashfs_self(hashfs); json_send_qstring(me ? sx_node_uuid(me)->string : "<UNKNOWN>"); CGI_PUTS(",\"ErrorId\":"); json_send_qstring(msg_get_id()); CGI_PUTC('}'); }
static int unbump_revs(struct revunbump_data_t *unb) { int64_t revs[MAX_UNBUMPS]; sqlite3_stmt *q; char *qry = NULL; const void *tgt; unsigned int i, qlen = 0, qat = 0; int64_t unbid; const sx_node_t *target, *me; int r, err = 0, remote; q = unb->quget_hi; sqlite3_reset(q); if(qbind_blob(q, ":oldtarget", (const void *)&unb->last_target, sizeof(unb->last_target))) return -1; /* Error */ r = qstep(q); if(r == SQLITE_DONE) { q = unb->quget_lo; sqlite3_reset(q); if(qbind_blob(q, ":oldtarget", (const void *)&unb->last_target, sizeof(unb->last_target))) return -1; /* Error */ r = qstep(q); if(r == SQLITE_DONE) return 0; /* Work complete */ } if(r != SQLITE_ROW) return -1; /* Error */ unbid = sqlite3_column_int64(q, 0); tgt = sqlite3_column_blob(q, 3); if(!tgt || sqlite3_column_bytes(q, 3) != sizeof(unb->last_target.binary)) { WARN("Removing unbid %lld with bogus target", (long long)unbid); sqlite3_reset(q); return unbump_unq(unb, unbid); } uuid_from_binary(&unb->last_target, tgt); target = sx_nodelist_lookup(sx_hashfs_all_nodes(unb->hashfs, NL_NEXTPREV), &unb->last_target); if(!target) { DEBUG("Removing unbid %lld for target node %s which is no longer a member", (long long)unbid, unb->last_target.string); sqlite3_reset(q); return unbump_unq(unb, unbid); } if(sx_hashfs_is_node_ignored(unb->hashfs, &unb->last_target)) { /* These will be sent once there is a replacement */ DEBUG("Skipping requests for unbid %lld for target node %s which is no longer a member", (long long)unbid, unb->last_target.string); sqlite3_reset(q); return 0; /* Work complete */ } me = sx_hashfs_self(unb->hashfs); remote = sx_node_cmp(me, target); if(!remote && sx_hashfs_revision_op_begin(unb->hashfs)) { WARN("Failed to start revision operation: %s", msg_get_reason()); sqlite3_reset(q); return -1; /* Error */ } for(i=0; i<MAX_UNBUMPS;) { const sx_hash_t *revid; unsigned int bs = sqlite3_column_int(q, 2); if(sx_hashfs_check_blocksize(bs)) WARN("Removing unbid %lld with invalid block size %u", (long long)unbid, bs); else if(!(revid = sqlite3_column_blob(q, 1)) || sqlite3_column_bytes(q, 1) != sizeof(*revid)) WARN("Removing unbid %lld with bogus revision ID", (long long)unbid); else if(!(tgt = sqlite3_column_blob(q, 3)) || sqlite3_column_bytes(q, 3) != sizeof(unb->last_target.binary)) WARN("Removing unbid %lld with bogus target", (long long)unbid); else if(memcmp(tgt, &unb->last_target.binary, sizeof(unb->last_target.binary))) break; else if(remote) { /* Remote target */ if(qlen - qat < sizeof(*revid) * 2 + sizeof(",\"\":") + 32) { /* Make room for hex encoded rev, size and json glue */ qlen += 1024; qry = wrap_realloc_or_free(qry, qlen); if(!qry) { WARN("Unable to allocate query"); err = 1; break; } } qry[qat] = qat ? ',' : '{'; qry[qat+1] = '"'; qat += 2; bin2hex(revid, sizeof(*revid), &qry[qat], qlen - qat); qat += sizeof(*revid)*2; qat += snprintf(&qry[qat], qlen - qat,"\":%u", bs); } else { /* Local target */ if(sx_hashfs_revision_op(unb->hashfs, bs, revid, -1) != OK) { WARN("Failed to unbump local revision"); err = 1; break; } } revs[i++] = sqlite3_column_int64(q, 0); r = qstep(q); if(r == SQLITE_ROW) continue; else if(r != SQLITE_DONE) { WARN("Failed to retrieve next revision"); err = 1; } break; } sqlite3_reset(q); if(!remote) { /* Commit local revision ops... */ if(!err && sx_hashfs_revision_op_commit(unb->hashfs)) { WARN("Failed to commit revision operation: %s", msg_get_reason()); err = 1; } /* ... or rollback on error */ if(err) sx_hashfs_revision_op_rollback(unb->hashfs); } if(err) { free(qry); return -1; /* Error */ } if(remote && qry) { sxi_conns_t *clust = sx_hashfs_conns(unb->hashfs); sxc_client_t *sx = sx_hashfs_client(unb->hashfs); sxi_hostlist_t hlist; int qret; sxi_hostlist_init(&hlist); if(qlen - qat < 2) { qry = wrap_realloc_or_free(qry, qlen + 2); if(!qry) { WARN("Unable to allocate query"); return -1; /* Error */ } } qry[qat] = '}'; qry[qat+1] = '\0'; if(sxi_hostlist_add_host(sx, &hlist, sx_node_internal_addr(target))) { WARN("Unable to allocate hostlist"); free(qry); return -1; /* Error */ } qret = sxi_cluster_query(clust, &hlist, REQ_PUT, ".blockrevs/remove", qry, strlen(qry), NULL, NULL, NULL); free(qry); qry = NULL; sxi_hostlist_empty(&hlist); if(qret != 200) { WARN("Unbump request failed for %s (%s): HTTP status %d", unb->last_target.string, sx_node_internal_addr(target), qret); return -1; } } free(qry); while(i--) unbump_unq(unb, revs[i]); return 1; /* Some work done */ }
static int schedule_blocks_sfq(struct blockmgr_data_t *q) { sx_uuid_t target_uuid; sqlite3_stmt *qget; int ret = 0, r; DEBUG("in %s", __func__); qget = q->qget_first_hi; if(qbind_int64(qget, ":flow", q->last_flowid)) { WARN("Error retrieving master block from queue"); return -1; } r = qstep(qget); if(r == SQLITE_DONE) { qget = q->qget_first_lo; if(qbind_int64(qget, ":flow", q->last_flowid)) { WARN("Error retrieving master block from queue"); return -1; } r = qstep(qget); } if(r == SQLITE_DONE) { DEBUG("No blocks in the queue"); return 0; } if(r != SQLITE_ROW) { WARN("Error retrieving master block from queue"); return -1; } do { int64_t push_id; const void *p; int i; /* SELECT id, flow, block[, size, node] */ push_id = sqlite3_column_int64(qget, 0); q->last_flowid = sqlite3_column_int64(qget, 1); if(!ret) { /* First block is the "master" and dictates blocksize and target node */ q->blocksize = sqlite3_column_int(qget, 3); if(sx_hashfs_check_blocksize(q->blocksize)) { WARN("Removing block with invalid blocksize %u", q->blocksize); sqlite3_reset(qget); blockmgr_del_xfer(q, push_id); return schedule_blocks_sfq(q); } p = sqlite3_column_blob(qget, 4); if(sqlite3_column_bytes(qget, 4) != sizeof(target_uuid.binary)) { WARN("Removing block with invalid target node UUID"); sqlite3_reset(qget); blockmgr_del_xfer(q, push_id); return schedule_blocks_sfq(q); } uuid_from_binary(&target_uuid, p); if(!(q->target = sx_nodelist_lookup(sx_hashfs_effective_nodes(q->hashfs, NL_NEXT), &target_uuid))) { DEBUG("Removing transfer to non existing (possibly ignored) node %s", target_uuid.string); sqlite3_reset(qget); blockmgr_del_xfer(q, push_id); return schedule_blocks_sfq(q); } if(!sx_node_cmp(q->target, sx_hashfs_self(q->hashfs))) { WARN("Removing transfer to self"); sqlite3_reset(qget); blockmgr_del_xfer(q, push_id); return schedule_blocks_sfq(q); } DEBUG("Selected master block for transfer bs: %u, node: %s", q->blocksize, target_uuid.string); } p = sqlite3_column_blob(qget, 2); if(sqlite3_column_bytes(qget, 2) != SXI_SHA1_BIN_LEN) { if(!ret) { /* Remove "master" block from queue */ WARN("Removing block with invalid hash"); sqlite3_reset(qget); blockmgr_del_xfer(q, push_id); return schedule_blocks_sfq(q); } else /* Or silently skip slaves (they'll be pruned in the subsequent loops) */ continue; } q->hashlist.ids[ret] = push_id; q->hashlist.havehs[ret] = 0; memcpy(&q->hashlist.binhs[ret], p, SXI_SHA1_BIN_LEN); sqlite3_reset(qget); if(!ret && qstep_noret(q->qwipesched)) { sqlite3_reset(qget); WARN("Failed to wipe schedule"); return -1; } if(qbind_int64(q->qaddsched, ":pushid", push_id) || qstep_noret(q->qaddsched)) { WARN("Failed to schedule block transfer"); return -1; } /* do { char hexh[SXI_SHA1_BIN_LEN * 2 + 1]; sxi_bin2hex(&q->hashlist.binhs[ret], SXI_SHA1_BIN_LEN, hexh); INFO("Block %s scheduled for transfer", hexh); } while(0); */ ret++; if(ret >= DOWNLOAD_MAX_BLOCKS) break; for(i = 0; i<2; i++) { /* Failure is not severe here: we just ship what we have scheduled so far and call it a day */ qget = (i == 0) ? q->qget_next_hi : q->qget_next_lo; if(qbind_int64(qget, ":flow", q->last_flowid) || qbind_int(qget, ":size", q->blocksize) || qbind_blob(qget, ":node", target_uuid.binary, sizeof(target_uuid.binary))) { WARN("Error retrieving next slave block from queue"); r = SQLITE_DONE; break; } r = qstep(qget); if(r == SQLITE_ROW) break; if(r != SQLITE_DONE) { WARN("Error retrieving next slave block from queue"); break; } } } while(r == SQLITE_ROW); q->hashlist.nblocks = ret; DEBUG("Successfully scheduled %d blocks for transfer", ret); return ret; }