/* return next server from the current tree, or a server from the init tree * if appropriate. If both trees are empty, return NULL. */ struct srv *get_next_server() { struct eb32_node *node; struct srv *s; node = eb32_first(&tree_0); s = eb32_entry(node, struct srv, node); if (!node || s->next > p) { /* either we have no server left, or we have a hole */ struct eb32_node *node2; node2 = eb32_first(init_tree); if (node2) { node = node2; s = eb32_entry(node, struct srv, node); get_srv_init(s); if (s->w == 0) node = NULL; s->node.key = 0; // do not display random values } } if (node) return s; else return NULL; }
/* return next server from the current tree in FWRR group <grp>, or a server * from the "init" tree if appropriate. If both trees are empty, return NULL. */ static struct server *fwrr_get_server_from_group(struct fwrr_group *grp) { struct eb32_node *node; struct server *s; node = eb32_first(&grp->curr); s = eb32_entry(node, struct server, lb_node); if (!node || s->npos > grp->curr_pos) { /* either we have no server left, or we have a hole */ struct eb32_node *node2; node2 = eb32_first(grp->init); if (node2) { node = node2; s = eb32_entry(node, struct server, lb_node); fwrr_get_srv_init(s); if (s->eweight == 0) /* FIXME: is it possible at all ? */ node = NULL; } } if (node) return s; else return NULL; }
/* Return next server from the FS tree in backend <p>. If the tree is empty, * return NULL. Saturated servers are skipped. */ struct server *fas_get_next_server(struct proxy *p, struct server *srvtoavoid) { struct server *srv, *avoided; struct eb32_node *node; srv = avoided = NULL; if (p->srv_act) node = eb32_first(&p->lbprm.fas.act); else if (p->lbprm.fbck) return p->lbprm.fbck; else if (p->srv_bck) node = eb32_first(&p->lbprm.fas.bck); else return NULL; while (node) { /* OK, we have a server. However, it may be saturated, in which * case we don't want to reconsider it for now, so we'll simply * skip it. Same if it's the server we try to avoid, in which * case we simply remember it for later use if needed. */ struct server *s; s = eb32_entry(node, struct server, lb_node); if (!s->maxconn || (!s->nbpend && s->served < srv_dynamic_maxconn(s))) { if (s != srvtoavoid) { srv = s; break; } avoided = s; } node = eb32_next(node); } if (!srv) srv = avoided; return srv; }
/* Retrieve the first pendconn from tree <pendconns>. Classes are always * considered first, then the time offset. The time does wrap, so the * lookup is performed twice, one to retrieve the first class and a second * time to retrieve the earliest time in this class. */ static struct pendconn *pendconn_first(struct eb_root *pendconns) { struct eb32_node *node, *node2 = NULL; u32 key; node = eb32_first(pendconns); if (!node) return NULL; key = KEY_CLASS_OFFSET_BOUNDARY(node->key); node2 = eb32_lookup_ge(pendconns, key); if (!node2 || KEY_CLASS(node2->key) != KEY_CLASS(node->key)) { /* no other key in the tree, or in this class */ return eb32_entry(node, struct pendconn, node); } /* found a better key */ return eb32_entry(node2, struct pendconn, node); }
/* Redistribute pending connections when a server goes down. The number of * connections redistributed is returned. It must be called with the server * lock held. */ int pendconn_redistribute(struct server *s) { struct pendconn *p; struct eb32_node *node; int xferred = 0; /* The REDISP option was specified. We will ignore cookie and force to * balance or use the dispatcher. */ if ((s->proxy->options & (PR_O_REDISP|PR_O_PERSIST)) != PR_O_REDISP) return 0; for (node = eb32_first(&s->pendconns); node; node = eb32_next(node)) { p = eb32_entry(&node, struct pendconn, node); if (p->strm_flags & SF_FORCE_PRST) continue; /* it's left to the dispatcher to choose a server */ __pendconn_unlink(p); p->strm_flags &= ~(SF_DIRECT | SF_ASSIGNED | SF_ADDR_SET); task_wakeup(p->strm->task, TASK_WOKEN_RES); } return xferred; }
/* * IO Handler to handle message exchance with a peer */ static void peer_io_handler(struct stream_interface *si) { struct task *t= (struct task *)si->owner; struct session *s = (struct session *)t->context; struct peers *curpeers = (struct peers *)s->fe->parent; int reql = 0; int repl = 0; while (1) { switchstate: switch(si->applet.st0) { case PEER_SESSION_ACCEPT: si->conn.data_ctx = NULL; si->applet.st0 = PEER_SESSION_GETVERSION; /* fall through */ case PEER_SESSION_GETVERSION: reql = bo_getline(si->ob, trash, trashlen); if (reql <= 0) { /* closed or EOL not found */ if (reql == 0) goto out; si->applet.st0 = PEER_SESSION_END; goto switchstate; } if (trash[reql-1] != '\n') { si->applet.st0 = PEER_SESSION_END; goto switchstate; } else if (reql > 1 && (trash[reql-2] == '\r')) trash[reql-2] = 0; else trash[reql-1] = 0; bo_skip(si->ob, reql); /* test version */ if (strcmp(PEER_SESSION_PROTO_NAME " 1.0", trash) != 0) { si->applet.st0 = PEER_SESSION_EXIT; si->applet.st1 = PEER_SESSION_ERRVERSION; /* test protocol */ if (strncmp(PEER_SESSION_PROTO_NAME " ", trash, strlen(PEER_SESSION_PROTO_NAME)+1) != 0) si->applet.st1 = PEER_SESSION_ERRPROTO; goto switchstate; } si->applet.st0 = PEER_SESSION_GETHOST; /* fall through */ case PEER_SESSION_GETHOST: reql = bo_getline(si->ob, trash, trashlen); if (reql <= 0) { /* closed or EOL not found */ if (reql == 0) goto out; si->applet.st0 = PEER_SESSION_END; goto switchstate; } if (trash[reql-1] != '\n') { si->applet.st0 = PEER_SESSION_END; goto switchstate; } else if (reql > 1 && (trash[reql-2] == '\r')) trash[reql-2] = 0; else trash[reql-1] = 0; bo_skip(si->ob, reql); /* test hostname match */ if (strcmp(localpeer, trash) != 0) { si->applet.st0 = PEER_SESSION_EXIT; si->applet.st1 = PEER_SESSION_ERRHOST; goto switchstate; } si->applet.st0 = PEER_SESSION_GETPEER; /* fall through */ case PEER_SESSION_GETPEER: { struct peer *curpeer; char *p; reql = bo_getline(si->ob, trash, trashlen); if (reql <= 0) { /* closed or EOL not found */ if (reql == 0) goto out; si->applet.st0 = PEER_SESSION_END; goto switchstate; } if (trash[reql-1] != '\n') { /* Incomplete line, we quit */ si->applet.st0 = PEER_SESSION_END; goto switchstate; } else if (reql > 1 && (trash[reql-2] == '\r')) trash[reql-2] = 0; else trash[reql-1] = 0; bo_skip(si->ob, reql); /* parse line "<peer name> <pid>" */ p = strchr(trash, ' '); if (!p) { si->applet.st0 = PEER_SESSION_EXIT; si->applet.st1 = PEER_SESSION_ERRPROTO; goto switchstate; } *p = 0; /* lookup known peer */ for (curpeer = curpeers->remote; curpeer; curpeer = curpeer->next) { if (strcmp(curpeer->id, trash) == 0) break; } /* if unknown peer */ if (!curpeer) { si->applet.st0 = PEER_SESSION_EXIT; si->applet.st1 = PEER_SESSION_ERRPEER; goto switchstate; } si->conn.data_ctx = curpeer; si->applet.st0 = PEER_SESSION_GETTABLE; /* fall through */ } case PEER_SESSION_GETTABLE: { struct peer *curpeer = (struct peer *)si->conn.data_ctx; struct shared_table *st; struct peer_session *ps = NULL; unsigned long key_type; size_t key_size; char *p; reql = bo_getline(si->ob, trash, trashlen); if (reql <= 0) { /* closed or EOL not found */ if (reql == 0) goto out; si->conn.data_ctx = NULL; si->applet.st0 = PEER_SESSION_END; goto switchstate; } /* Re init si->conn.data_ctx to null, to handle correctly a release case */ si->conn.data_ctx = NULL; if (trash[reql-1] != '\n') { /* Incomplete line, we quit */ si->applet.st0 = PEER_SESSION_END; goto switchstate; } else if (reql > 1 && (trash[reql-2] == '\r')) trash[reql-2] = 0; else trash[reql-1] = 0; bo_skip(si->ob, reql); /* Parse line "<table name> <type> <size>" */ p = strchr(trash, ' '); if (!p) { si->applet.st0 = PEER_SESSION_EXIT; si->applet.st1 = PEER_SESSION_ERRPROTO; goto switchstate; } *p = 0; key_type = (unsigned long)atol(p+1); p = strchr(p+1, ' '); if (!p) { si->conn.data_ctx = NULL; si->applet.st0 = PEER_SESSION_EXIT; si->applet.st1 = PEER_SESSION_ERRPROTO; goto switchstate; } key_size = (size_t)atoi(p); for (st = curpeers->tables; st; st = st->next) { /* If table name matches */ if (strcmp(st->table->id, trash) == 0) { /* If key size mismatches */ if (key_size != st->table->key_size) { si->applet.st0 = PEER_SESSION_EXIT; si->applet.st1 = PEER_SESSION_ERRSIZE; goto switchstate; } /* If key type mismatches */ if (key_type != st->table->type) { si->applet.st0 = PEER_SESSION_EXIT; si->applet.st1 = PEER_SESSION_ERRTYPE; goto switchstate; } /* lookup peer session of current peer */ for (ps = st->sessions; ps; ps = ps->next) { if (ps->peer == curpeer) { /* If session already active, replaced by new one */ if (ps->session && ps->session != s) { if (ps->peer->local) { /* Local connection, reply a retry */ si->applet.st0 = PEER_SESSION_EXIT; si->applet.st1 = PEER_SESSION_TRYAGAIN; goto switchstate; } peer_session_forceshutdown(ps->session); } ps->session = s; break; } } break; } } /* If table not found */ if (!st){ si->applet.st0 = PEER_SESSION_EXIT; si->applet.st1 = PEER_SESSION_ERRTABLE; goto switchstate; } /* If no peer session for current peer */ if (!ps) { si->applet.st0 = PEER_SESSION_EXIT; si->applet.st1 = PEER_SESSION_ERRPEER; goto switchstate; } si->conn.data_ctx = ps; si->applet.st0 = PEER_SESSION_SENDSUCCESS; /* fall through */ } case PEER_SESSION_SENDSUCCESS:{ struct peer_session *ps = (struct peer_session *)si->conn.data_ctx; repl = snprintf(trash, trashlen, "%d\n", PEER_SESSION_SUCCESSCODE); repl = bi_putblk(si->ib, trash, repl); if (repl <= 0) { if (repl == -1) goto out; si->applet.st0 = PEER_SESSION_END; goto switchstate; } /* Register status code */ ps->statuscode = PEER_SESSION_SUCCESSCODE; /* Awake main task */ task_wakeup(ps->table->sync_task, TASK_WOKEN_MSG); /* Init cursors */ ps->teaching_origin =ps->lastpush = ps->lastack = ps->pushack = 0; ps->pushed = ps->update; /* Init confirm counter */ ps->confirm = 0; /* reset teaching and learning flags to 0 */ ps->flags &= PEER_TEACH_RESET; ps->flags &= PEER_LEARN_RESET; /* if current peer is local */ if (ps->peer->local) { /* if table need resyncfrom local and no process assined */ if ((ps->table->flags & SHTABLE_RESYNC_STATEMASK) == SHTABLE_RESYNC_FROMLOCAL && !(ps->table->flags & SHTABLE_F_RESYNC_ASSIGN)) { /* assign local peer for a lesson, consider lesson already requested */ ps->flags |= PEER_F_LEARN_ASSIGN; ps->table->flags |= (SHTABLE_F_RESYNC_ASSIGN|SHTABLE_F_RESYNC_PROCESS); } } else if ((ps->table->flags & SHTABLE_RESYNC_STATEMASK) == SHTABLE_RESYNC_FROMREMOTE && !(ps->table->flags & SHTABLE_F_RESYNC_ASSIGN)) { /* assign peer for a lesson */ ps->flags |= PEER_F_LEARN_ASSIGN; ps->table->flags |= SHTABLE_F_RESYNC_ASSIGN; } /* switch to waiting message state */ si->applet.st0 = PEER_SESSION_WAITMSG; goto switchstate; } case PEER_SESSION_CONNECT: { struct peer_session *ps = (struct peer_session *)si->conn.data_ctx; /* Send headers */ repl = snprintf(trash, trashlen, PEER_SESSION_PROTO_NAME " 1.0\n%s\n%s %d\n%s %lu %d\n", ps->peer->id, localpeer, (int)getpid(), ps->table->table->id, ps->table->table->type, (int)ps->table->table->key_size); if (repl >= trashlen) { si->applet.st0 = PEER_SESSION_END; goto switchstate; } repl = bi_putblk(si->ib, trash, repl); if (repl <= 0) { if (repl == -1) goto out; si->applet.st0 = PEER_SESSION_END; goto switchstate; } /* switch to the waiting statuscode state */ si->applet.st0 = PEER_SESSION_GETSTATUS; /* fall through */ } case PEER_SESSION_GETSTATUS: { struct peer_session *ps = (struct peer_session *)si->conn.data_ctx; if (si->ib->flags & CF_WRITE_PARTIAL) ps->statuscode = PEER_SESSION_CONNECTEDCODE; reql = bo_getline(si->ob, trash, trashlen); if (reql <= 0) { /* closed or EOL not found */ if (reql == 0) goto out; si->applet.st0 = PEER_SESSION_END; goto switchstate; } if (trash[reql-1] != '\n') { /* Incomplete line, we quit */ si->applet.st0 = PEER_SESSION_END; goto switchstate; } else if (reql > 1 && (trash[reql-2] == '\r')) trash[reql-2] = 0; else trash[reql-1] = 0; bo_skip(si->ob, reql); /* Register status code */ ps->statuscode = atoi(trash); /* Awake main task */ task_wakeup(ps->table->sync_task, TASK_WOKEN_MSG); /* If status code is success */ if (ps->statuscode == PEER_SESSION_SUCCESSCODE) { /* Init cursors */ ps->teaching_origin = ps->lastpush = ps->lastack = ps->pushack = 0; ps->pushed = ps->update; /* Init confirm counter */ ps->confirm = 0; /* reset teaching and learning flags to 0 */ ps->flags &= PEER_TEACH_RESET; ps->flags &= PEER_LEARN_RESET; /* If current peer is local */ if (ps->peer->local) { /* Init cursors to push a resync */ ps->teaching_origin = ps->pushed = ps->table->table->update; /* flag to start to teach lesson */ ps->flags |= PEER_F_TEACH_PROCESS; } else if ((ps->table->flags & SHTABLE_RESYNC_STATEMASK) == SHTABLE_RESYNC_FROMREMOTE && !(ps->table->flags & SHTABLE_F_RESYNC_ASSIGN)) { /* If peer is remote and resync from remote is needed, and no peer currently assigned */ /* assign peer for a lesson */ ps->flags |= PEER_F_LEARN_ASSIGN; ps->table->flags |= SHTABLE_F_RESYNC_ASSIGN; } } else { /* Status code is not success, abort */ si->applet.st0 = PEER_SESSION_END; goto switchstate; } si->applet.st0 = PEER_SESSION_WAITMSG; /* fall through */ } case PEER_SESSION_WAITMSG: { struct peer_session *ps = (struct peer_session *)si->conn.data_ctx; char c; int totl = 0; reql = bo_getblk(si->ob, (char *)&c, sizeof(c), totl); if (reql <= 0) { /* closed or EOL not found */ if (reql == 0) { /* nothing to read */ goto incomplete; } si->applet.st0 = PEER_SESSION_END; goto switchstate; } totl += reql; if ((c & 0x80) || (c == 'D')) { /* Here we have data message */ unsigned int pushack; struct stksess *ts; struct stksess *newts; struct stktable_key stkey; int srvid; uint32_t netinteger; /* Compute update remote version */ if (c & 0x80) { pushack = ps->pushack + (unsigned int)(c & 0x7F); } else { reql = bo_getblk(si->ob, (char *)&netinteger, sizeof(netinteger), totl); if (reql <= 0) { /* closed or EOL not found */ if (reql == 0) { goto incomplete; } si->applet.st0 = PEER_SESSION_END; goto switchstate; } totl += reql; pushack = ntohl(netinteger); } /* read key */ if (ps->table->table->type == STKTABLE_TYPE_STRING) { /* type string */ stkey.key = stkey.data.buf; reql = bo_getblk(si->ob, (char *)&netinteger, sizeof(netinteger), totl); if (reql <= 0) { /* closed or EOL not found */ if (reql == 0) { goto incomplete; } si->applet.st0 = PEER_SESSION_END; goto switchstate; } totl += reql; stkey.key_len = ntohl(netinteger); reql = bo_getblk(si->ob, stkey.key, stkey.key_len, totl); if (reql <= 0) { /* closed or EOL not found */ if (reql == 0) { goto incomplete; } si->applet.st0 = PEER_SESSION_END; goto switchstate; } totl += reql; } else if (ps->table->table->type == STKTABLE_TYPE_INTEGER) { /* type integer */ stkey.key_len = (size_t)-1; stkey.key = &stkey.data.integer; reql = bo_getblk(si->ob, (char *)&netinteger, sizeof(netinteger), totl); if (reql <= 0) { /* closed or EOL not found */ if (reql == 0) { goto incomplete; } si->applet.st0 = PEER_SESSION_END; goto switchstate; } totl += reql; stkey.data.integer = ntohl(netinteger); } else { /* type ip */ stkey.key_len = (size_t)-1; stkey.key = stkey.data.buf; reql = bo_getblk(si->ob, (char *)&stkey.data.buf, ps->table->table->key_size, totl); if (reql <= 0) { /* closed or EOL not found */ if (reql == 0) { goto incomplete; } si->applet.st0 = PEER_SESSION_END; goto switchstate; } totl += reql; } /* read server id */ reql = bo_getblk(si->ob, (char *)&netinteger, sizeof(netinteger), totl); if (reql <= 0) { /* closed or EOL not found */ if (reql == 0) { goto incomplete; } si->applet.st0 = PEER_SESSION_END; goto switchstate; } totl += reql; srvid = ntohl(netinteger); /* update entry */ newts = stksess_new(ps->table->table, &stkey); if (newts) { /* lookup for existing entry */ ts = stktable_lookup(ps->table->table, newts); if (ts) { /* the entry already exist, we can free ours */ stktable_touch(ps->table->table, ts, 0); stksess_free(ps->table->table, newts); } else { struct eb32_node *eb; /* create new entry */ ts = stktable_store(ps->table->table, newts, 0); ts->upd.key= (++ps->table->table->update)+(2^31); eb = eb32_insert(&ps->table->table->updates, &ts->upd); if (eb != &ts->upd) { eb32_delete(eb); eb32_insert(&ps->table->table->updates, &ts->upd); } } /* update entry */ if (srvid && stktable_data_ptr(ps->table->table, ts, STKTABLE_DT_SERVER_ID)) stktable_data_cast(stktable_data_ptr(ps->table->table, ts, STKTABLE_DT_SERVER_ID), server_id) = srvid; ps->pushack = pushack; } } else if (c == 'R') { /* Reset message: remote need resync */ /* reinit counters for a resync */ ps->lastpush = 0; ps->teaching_origin = ps->pushed = ps->table->table->update; /* reset teaching flags to 0 */ ps->flags &= PEER_TEACH_RESET; /* flag to start to teach lesson */ ps->flags |= PEER_F_TEACH_PROCESS; } else if (c == 'F') { /* Finish message, all known updates have been pushed by remote */ /* and remote is up to date */ /* If resync is in progress with remote peer */ if (ps->flags & PEER_F_LEARN_ASSIGN) { /* unassign current peer for learning */ ps->flags &= ~PEER_F_LEARN_ASSIGN; ps->table->flags &= ~(SHTABLE_F_RESYNC_ASSIGN|SHTABLE_F_RESYNC_PROCESS); /* Consider table is now up2date, resync resync no more needed from local neither remote */ ps->table->flags |= (SHTABLE_F_RESYNC_LOCAL|SHTABLE_F_RESYNC_REMOTE); } /* Increase confirm counter to launch a confirm message */ ps->confirm++; } else if (c == 'c') { /* confirm message, remote peer is now up to date with us */ /* If stopping state */ if (stopping) { /* Close session, push resync no more needed */ ps->flags |= PEER_F_TEACH_COMPLETE; si->applet.st0 = PEER_SESSION_END; goto switchstate; } /* reset teaching flags to 0 */ ps->flags &= PEER_TEACH_RESET; } else if (c == 'C') { /* Continue message, all known updates have been pushed by remote */ /* but remote is not up to date */ /* If resync is in progress with current peer */ if (ps->flags & PEER_F_LEARN_ASSIGN) { /* unassign current peer */ ps->flags &= ~PEER_F_LEARN_ASSIGN; ps->table->flags &= ~(SHTABLE_F_RESYNC_ASSIGN|SHTABLE_F_RESYNC_PROCESS); /* flag current peer is not up 2 date to try from an other */ ps->flags |= PEER_F_LEARN_NOTUP2DATE; /* reschedule a resync */ ps->table->resync_timeout = tick_add(now_ms, MS_TO_TICKS(5000)); task_wakeup(ps->table->sync_task, TASK_WOKEN_MSG); } ps->confirm++; } else if (c == 'A') { /* ack message */ uint32_t netinteger; reql = bo_getblk(si->ob, (char *)&netinteger, sizeof(netinteger), totl); if (reql <= 0) { /* closed or EOL not found */ if (reql == 0) { goto incomplete; } si->applet.st0 = PEER_SESSION_END; goto switchstate; } totl += reql; /* Consider remote is up to date with "acked" version */ ps->update = ntohl(netinteger); } else { /* Unknown message */ si->applet.st0 = PEER_SESSION_END; goto switchstate; } /* skip consumed message */ bo_skip(si->ob, totl); /* loop on that state to peek next message */ continue; incomplete: /* Nothing to read, now we start to write */ /* Confirm finished or partial messages */ while (ps->confirm) { /* There is a confirm messages to send */ repl = bi_putchr(si->ib, 'c'); if (repl <= 0) { /* no more write possible */ if (repl == -1) goto out; si->applet.st0 = PEER_SESSION_END; goto switchstate; } ps->confirm--; } /* Need to request a resync */ if ((ps->flags & PEER_F_LEARN_ASSIGN) && (ps->table->flags & SHTABLE_F_RESYNC_ASSIGN) && !(ps->table->flags & SHTABLE_F_RESYNC_PROCESS)) { /* Current peer was elected to request a resync */ repl = bi_putchr(si->ib, 'R'); if (repl <= 0) { /* no more write possible */ if (repl == -1) goto out; si->applet.st0 = PEER_SESSION_END; goto switchstate; } ps->table->flags |= SHTABLE_F_RESYNC_PROCESS; } /* It remains some updates to ack */ if (ps->pushack != ps->lastack) { uint32_t netinteger; trash[0] = 'A'; netinteger = htonl(ps->pushack); memcpy(&trash[1], &netinteger, sizeof(netinteger)); repl = bi_putblk(si->ib, trash, 1+sizeof(netinteger)); if (repl <= 0) { /* no more write possible */ if (repl == -1) goto out; si->applet.st0 = PEER_SESSION_END; goto switchstate; } ps->lastack = ps->pushack; } if (ps->flags & PEER_F_TEACH_PROCESS) { /* current peer was requested for a lesson */ if (!(ps->flags & PEER_F_TEACH_STAGE1)) { /* lesson stage 1 not complete */ struct eb32_node *eb; eb = eb32_lookup_ge(&ps->table->table->updates, ps->pushed+1); while (1) { int msglen; struct stksess *ts; if (!eb) { /* flag lesson stage1 complete */ ps->flags |= PEER_F_TEACH_STAGE1; eb = eb32_first(&ps->table->table->updates); if (eb) ps->pushed = eb->key - 1; break; } ts = eb32_entry(eb, struct stksess, upd); msglen = peer_prepare_datamsg(ts, ps, trash, trashlen); if (msglen) { /* message to buffer */ repl = bi_putblk(si->ib, trash, msglen); if (repl <= 0) { /* no more write possible */ if (repl == -1) goto out; si->applet.st0 = PEER_SESSION_END; goto switchstate; } ps->lastpush = ps->pushed = ts->upd.key; } eb = eb32_next(eb); } } /* !TEACH_STAGE1 */ if (!(ps->flags & PEER_F_TEACH_STAGE2)) { /* lesson stage 2 not complete */ struct eb32_node *eb; eb = eb32_lookup_ge(&ps->table->table->updates, ps->pushed+1); while (1) { int msglen; struct stksess *ts; if (!eb || eb->key > ps->teaching_origin) { /* flag lesson stage1 complete */ ps->flags |= PEER_F_TEACH_STAGE2; ps->pushed = ps->teaching_origin; break; } ts = eb32_entry(eb, struct stksess, upd); msglen = peer_prepare_datamsg(ts, ps, trash, trashlen); if (msglen) { /* message to buffer */ repl = bi_putblk(si->ib, trash, msglen); if (repl <= 0) { /* no more write possible */ if (repl == -1) goto out; si->applet.st0 = PEER_SESSION_END; goto switchstate; } ps->lastpush = ps->pushed = ts->upd.key; } eb = eb32_next(eb); } } /* !TEACH_STAGE2 */ if (!(ps->flags & PEER_F_TEACH_FINISHED)) { /* process final lesson message */ repl = bi_putchr(si->ib, ((ps->table->flags & SHTABLE_RESYNC_STATEMASK) == SHTABLE_RESYNC_FINISHED) ? 'F' : 'C'); if (repl <= 0) { /* no more write possible */ if (repl == -1) goto out; si->applet.st0 = PEER_SESSION_END; goto switchstate; } /* flag finished message sent */ ps->flags |= PEER_F_TEACH_FINISHED; } /* !TEACH_FINISHED */ } /* TEACH_PROCESS */ if (!(ps->flags & PEER_F_LEARN_ASSIGN) && (int)(ps->pushed - ps->table->table->localupdate) < 0) { /* Push local updates, only if no learning in progress (to avoid ping-pong effects) */ struct eb32_node *eb; eb = eb32_lookup_ge(&ps->table->table->updates, ps->pushed+1); while (1) { int msglen; struct stksess *ts; /* push local updates */ if (!eb) { eb = eb32_first(&ps->table->table->updates); if (!eb || ((int)(eb->key - ps->pushed) <= 0)) { ps->pushed = ps->table->table->localupdate; break; } } if ((int)(eb->key - ps->table->table->localupdate) > 0) { ps->pushed = ps->table->table->localupdate; break; } ts = eb32_entry(eb, struct stksess, upd); msglen = peer_prepare_datamsg(ts, ps, trash, trashlen); if (msglen) { /* message to buffer */ repl = bi_putblk(si->ib, trash, msglen); if (repl <= 0) { /* no more write possible */ if (repl == -1) goto out; si->applet.st0 = PEER_SESSION_END; goto switchstate; } ps->lastpush = ps->pushed = ts->upd.key; } eb = eb32_next(eb); } } /* ! LEARN_ASSIGN */ /* noting more to do */ goto out; } case PEER_SESSION_EXIT: repl = snprintf(trash, trashlen, "%d\n", si->applet.st1); if (bi_putblk(si->ib, trash, repl) == -1) goto out; si->applet.st0 = PEER_SESSION_END; /* fall through */ case PEER_SESSION_END: { si_shutw(si); si_shutr(si); si->ib->flags |= CF_READ_NULL; goto quit; } } } out: si_update(si); si->ob->flags |= CF_READ_DONTWAIT; /* we don't want to expire timeouts while we're processing requests */ si->ib->rex = TICK_ETERNITY; si->ob->wex = TICK_ETERNITY; quit: return; }
int main(int argc, char **argv) { const char *b, *e, *p; const char *output_file = NULL; int f, tot, last, linenum, err, parse_err; struct timer *t = NULL, *t2; struct eb32_node *n; int val, test; int array[5]; int filter_acc_delay = 0, filter_acc_count = 0; int filter_time_resp = 0; int skip_fields = 1; argc--; argv++; while (argc > 0) { if (*argv[0] != '-') break; if (strcmp(argv[0], "-ad") == 0) { if (argc < 2) die("missing option for -ad"); argc--; argv++; filter |= FILT_ACC_DELAY; filter_acc_delay = atol(*argv); } else if (strcmp(argv[0], "-ac") == 0) { if (argc < 2) die("missing option for -ac"); argc--; argv++; filter |= FILT_ACC_COUNT; filter_acc_count = atol(*argv); } else if (strcmp(argv[0], "-rt") == 0) { if (argc < 2) die("missing option for -rt"); argc--; argv++; filter |= FILT_TIME_RESP; filter_time_resp = atol(*argv); } else if (strcmp(argv[0], "-RT") == 0) { if (argc < 2) die("missing option for -RT"); argc--; argv++; filter |= FILT_TIME_RESP | FILT_INVERT_TIME_RESP; filter_time_resp = atol(*argv); } else if (strcmp(argv[0], "-s") == 0) { if (argc < 2) die("missing option for -s"); argc--; argv++; skip_fields = atol(*argv); } else if (strcmp(argv[0], "-e") == 0) filter |= FILT_ERRORS_ONLY; else if (strcmp(argv[0], "-E") == 0) filter |= FILT_ERRORS_ONLY | FILT_INVERT_ERRORS; else if (strcmp(argv[0], "-c") == 0) filter |= FILT_COUNT_ONLY; else if (strcmp(argv[0], "-q") == 0) filter |= FILT_QUIET; else if (strcmp(argv[0], "-v") == 0) filter_invert = !filter_invert; else if (strcmp(argv[0], "-gt") == 0) filter |= FILT_GRAPH_TIMERS; else if (strcmp(argv[0], "-pct") == 0) filter |= FILT_PERCENTILE; else if (strcmp(argv[0], "-o") == 0) { if (output_file) die("Fatal: output file name already specified.\n"); if (argc < 2) die("Fatal: missing output file name.\n"); output_file = argv[1]; } argc--; argv++; } if (!filter) die("No action specified.\n"); if (filter & FILT_ACC_COUNT && !filter_acc_count) filter_acc_count=1; if (filter & FILT_ACC_DELAY && !filter_acc_delay) filter_acc_delay = 1; linenum = 0; tot = 0; parse_err = 0; while ((line = fgets2(stdin)) != NULL) { linenum++; test = 1; if (filter & FILT_TIME_RESP) { int tps; /* only report lines with response times larger than filter_time_resp */ b = field_start(line, TIME_FIELD + skip_fields); if (!*b) { truncated_line(linenum, line); continue; } e = field_stop(b + 1); /* we have field TIME_FIELD in [b]..[e-1] */ p = b; err = 0; for (f = 0; f < 4 && *p; f++) { tps = str2ic(p); if (tps < 0) { tps = -1; err = 1; } SKIP_CHAR(p, '/'); } if (f < 4) { parse_err++; continue; } test &= (tps >= filter_time_resp) ^ !!(filter & FILT_INVERT_TIME_RESP); } if (filter & FILT_ERRORS_ONLY) { /* only report erroneous status codes */ b = field_start(line, STATUS_FIELD + skip_fields); if (!*b) { truncated_line(linenum, line); continue; } if (*b == '-') { test &= !!(filter & FILT_INVERT_ERRORS); } else { val = strl2ui(b, 3); test &= (val >= 500 && val <= 599) ^ !!(filter & FILT_INVERT_ERRORS); } } if (filter & (FILT_ACC_COUNT|FILT_ACC_DELAY)) { b = field_start(line, ACCEPT_FIELD + skip_fields); if (!*b) { truncated_line(linenum, line); continue; } tot++; val = convert_date(b); //printf("date=%s => %d\n", b, val); if (val < 0) { parse_err++; continue; } t2 = insert_value(&timers[0], &t, val); t2->count++; continue; } if (filter & (FILT_GRAPH_TIMERS|FILT_PERCENTILE)) { int f; b = field_start(line, TIME_FIELD + skip_fields); if (!*b) { truncated_line(linenum, line); continue; } e = field_stop(b + 1); /* we have field TIME_FIELD in [b]..[e-1] */ p = b; err = 0; for (f = 0; f < 5 && *p; f++) { array[f] = str2ic(p); if (array[f] < 0) { array[f] = -1; err = 1; } SKIP_CHAR(p, '/'); } if (f < 5) { parse_err++; continue; } /* if we find at least one negative time, we count one error * with a time equal to the total session time. This will * emphasize quantum timing effects associated to known * timeouts. Note that on some buggy machines, it is possible * that the total time is negative, hence the reason to reset * it. */ if (filter & FILT_GRAPH_TIMERS) { if (err) { if (array[4] < 0) array[4] = -1; t2 = insert_timer(&timers[0], &t, array[4]); // total time t2->count++; } else { int v; t2 = insert_timer(&timers[1], &t, array[0]); t2->count++; // req t2 = insert_timer(&timers[2], &t, array[2]); t2->count++; // conn t2 = insert_timer(&timers[3], &t, array[3]); t2->count++; // resp v = array[4] - array[0] - array[1] - array[2] - array[3]; // data time if (v < 0 && !(filter & FILT_QUIET)) fprintf(stderr, "ERR: %s (%d %d %d %d %d => %d)\n", line, array[0], array[1], array[2], array[3], array[4], v); t2 = insert_timer(&timers[4], &t, v); t2->count++; tot++; } } else { /* percentile */ if (err) { if (array[4] < 0) array[4] = -1; t2 = insert_value(&timers[0], &t, array[4]); // total time t2->count++; } else { int v; t2 = insert_value(&timers[1], &t, array[0]); t2->count++; // req t2 = insert_value(&timers[2], &t, array[2]); t2->count++; // conn t2 = insert_value(&timers[3], &t, array[3]); t2->count++; // resp v = array[4] - array[0] - array[1] - array[2] - array[3]; // data time if (v < 0 && !(filter & FILT_QUIET)) fprintf(stderr, "ERR: %s (%d %d %d %d %d => %d)\n", line, array[0], array[1], array[2], array[3], array[4], v); t2 = insert_value(&timers[4], &t, v); t2->count++; tot++; } } continue; } test ^= filter_invert; if (!test) continue; /* all other cases mean we just want to count lines */ tot++; if (!(filter & FILT_COUNT_ONLY)) puts(line); } if (t) free(t); if (filter & FILT_COUNT_ONLY) { printf("%d\n", tot); exit(0); } if (filter & FILT_ERRORS_ONLY) exit(0); if (filter & (FILT_ACC_COUNT|FILT_ACC_DELAY)) { /* sort and count all timers. Output will look like this : * <accept_date> <delta_ms from previous one> <nb entries> */ n = eb32_first(&timers[0]); if (n) last = n->key; while (n) { unsigned int d, h, m, s, ms; t = container_of(n, struct timer, node); h = n->key; d = h - last; last = h; if (d >= filter_acc_delay && t->count >= filter_acc_count) { ms = h % 1000; h = h / 1000; s = h % 60; h = h / 60; m = h % 60; h = h / 60; tot++; printf("%02d:%02d:%02d.%03d %d %d %d\n", h, m, s, ms, last, d, t->count); } n = eb32_next(n); } }
/* * IO Handler to handle message exchance with a peer */ static void peer_io_handler(struct appctx *appctx) { struct stream_interface *si = appctx->owner; struct stream *s = si_strm(si); struct peers *curpeers = (struct peers *)strm_fe(s)->parent; int reql = 0; int repl = 0; while (1) { switchstate: switch(appctx->st0) { case PEER_SESS_ST_ACCEPT: appctx->ctx.peers.ptr = NULL; appctx->st0 = PEER_SESS_ST_GETVERSION; /* fall through */ case PEER_SESS_ST_GETVERSION: reql = bo_getline(si_oc(si), trash.str, trash.size); if (reql <= 0) { /* closed or EOL not found */ if (reql == 0) goto out; appctx->st0 = PEER_SESS_ST_END; goto switchstate; } if (trash.str[reql-1] != '\n') { appctx->st0 = PEER_SESS_ST_END; goto switchstate; } else if (reql > 1 && (trash.str[reql-2] == '\r')) trash.str[reql-2] = 0; else trash.str[reql-1] = 0; bo_skip(si_oc(si), reql); /* test version */ if (strcmp(PEER_SESSION_PROTO_NAME " 1.0", trash.str) != 0) { appctx->st0 = PEER_SESS_ST_EXIT; appctx->st1 = PEER_SESS_SC_ERRVERSION; /* test protocol */ if (strncmp(PEER_SESSION_PROTO_NAME " ", trash.str, strlen(PEER_SESSION_PROTO_NAME)+1) != 0) appctx->st1 = PEER_SESS_SC_ERRPROTO; goto switchstate; } appctx->st0 = PEER_SESS_ST_GETHOST; /* fall through */ case PEER_SESS_ST_GETHOST: reql = bo_getline(si_oc(si), trash.str, trash.size); if (reql <= 0) { /* closed or EOL not found */ if (reql == 0) goto out; appctx->st0 = PEER_SESS_ST_END; goto switchstate; } if (trash.str[reql-1] != '\n') { appctx->st0 = PEER_SESS_ST_END; goto switchstate; } else if (reql > 1 && (trash.str[reql-2] == '\r')) trash.str[reql-2] = 0; else trash.str[reql-1] = 0; bo_skip(si_oc(si), reql); /* test hostname match */ if (strcmp(localpeer, trash.str) != 0) { appctx->st0 = PEER_SESS_ST_EXIT; appctx->st1 = PEER_SESS_SC_ERRHOST; goto switchstate; } appctx->st0 = PEER_SESS_ST_GETPEER; /* fall through */ case PEER_SESS_ST_GETPEER: { struct peer *curpeer; char *p; reql = bo_getline(si_oc(si), trash.str, trash.size); if (reql <= 0) { /* closed or EOL not found */ if (reql == 0) goto out; appctx->st0 = PEER_SESS_ST_END; goto switchstate; } if (trash.str[reql-1] != '\n') { /* Incomplete line, we quit */ appctx->st0 = PEER_SESS_ST_END; goto switchstate; } else if (reql > 1 && (trash.str[reql-2] == '\r')) trash.str[reql-2] = 0; else trash.str[reql-1] = 0; bo_skip(si_oc(si), reql); /* parse line "<peer name> <pid>" */ p = strchr(trash.str, ' '); if (!p) { appctx->st0 = PEER_SESS_ST_EXIT; appctx->st1 = PEER_SESS_SC_ERRPROTO; goto switchstate; } *p = 0; /* lookup known peer */ for (curpeer = curpeers->remote; curpeer; curpeer = curpeer->next) { if (strcmp(curpeer->id, trash.str) == 0) break; } /* if unknown peer */ if (!curpeer) { appctx->st0 = PEER_SESS_ST_EXIT; appctx->st1 = PEER_SESS_SC_ERRPEER; goto switchstate; } appctx->ctx.peers.ptr = curpeer; appctx->st0 = PEER_SESS_ST_GETTABLE; /* fall through */ } case PEER_SESS_ST_GETTABLE: { struct peer *curpeer = (struct peer *)appctx->ctx.peers.ptr; struct shared_table *st; struct peer_session *ps = NULL; unsigned long key_type; size_t key_size; char *p; reql = bo_getline(si_oc(si), trash.str, trash.size); if (reql <= 0) { /* closed or EOL not found */ if (reql == 0) goto out; appctx->ctx.peers.ptr = NULL; appctx->st0 = PEER_SESS_ST_END; goto switchstate; } /* Re init appctx->ctx.peers.ptr to null, to handle correctly a release case */ appctx->ctx.peers.ptr = NULL; if (trash.str[reql-1] != '\n') { /* Incomplete line, we quit */ appctx->st0 = PEER_SESS_ST_END; goto switchstate; } else if (reql > 1 && (trash.str[reql-2] == '\r')) trash.str[reql-2] = 0; else trash.str[reql-1] = 0; bo_skip(si_oc(si), reql); /* Parse line "<table name> <type> <size>" */ p = strchr(trash.str, ' '); if (!p) { appctx->st0 = PEER_SESS_ST_EXIT; appctx->st1 = PEER_SESS_SC_ERRPROTO; goto switchstate; } *p = 0; key_type = (unsigned long)atol(p+1); p = strchr(p+1, ' '); if (!p) { appctx->ctx.peers.ptr = NULL; appctx->st0 = PEER_SESS_ST_EXIT; appctx->st1 = PEER_SESS_SC_ERRPROTO; goto switchstate; } key_size = (size_t)atoi(p); for (st = curpeers->tables; st; st = st->next) { /* If table name matches */ if (strcmp(st->table->id, trash.str) == 0) { /* Check key size mismatches, except for strings * which may be truncated as long as they fit in * a buffer. */ if (key_size != st->table->key_size && (key_type != STKTABLE_TYPE_STRING || 1 + 4 + 4 + key_size - 1 >= trash.size)) { appctx->st0 = PEER_SESS_ST_EXIT; appctx->st1 = PEER_SESS_SC_ERRSIZE; goto switchstate; } /* If key type mismatches */ if (key_type != st->table->type) { appctx->st0 = PEER_SESS_ST_EXIT; appctx->st1 = PEER_SESS_SC_ERRTYPE; goto switchstate; } /* lookup peer stream of current peer */ for (ps = st->sessions; ps; ps = ps->next) { if (ps->peer == curpeer) { /* If stream already active, replaced by new one */ if (ps->stream && ps->stream != s) { if (ps->peer->local) { /* Local connection, reply a retry */ appctx->st0 = PEER_SESS_ST_EXIT; appctx->st1 = PEER_SESS_SC_TRYAGAIN; goto switchstate; } peer_session_forceshutdown(ps->stream); } ps->stream = s; ps->appctx = appctx; break; } } break; } } /* If table not found */ if (!st){ appctx->st0 = PEER_SESS_ST_EXIT; appctx->st1 = PEER_SESS_SC_ERRTABLE; goto switchstate; } /* If no peer session for current peer */ if (!ps) { appctx->st0 = PEER_SESS_ST_EXIT; appctx->st1 = PEER_SESS_SC_ERRPEER; goto switchstate; } appctx->ctx.peers.ptr = ps; appctx->st0 = PEER_SESS_ST_SENDSUCCESS; /* fall through */ } case PEER_SESS_ST_SENDSUCCESS: { struct peer_session *ps = (struct peer_session *)appctx->ctx.peers.ptr; repl = snprintf(trash.str, trash.size, "%d\n", PEER_SESS_SC_SUCCESSCODE); repl = bi_putblk(si_ic(si), trash.str, repl); if (repl <= 0) { if (repl == -1) goto full; appctx->st0 = PEER_SESS_ST_END; goto switchstate; } /* Register status code */ ps->statuscode = PEER_SESS_SC_SUCCESSCODE; /* Awake main task */ task_wakeup(ps->table->sync_task, TASK_WOKEN_MSG); /* Init cursors */ ps->teaching_origin =ps->lastpush = ps->lastack = ps->pushack = 0; ps->pushed = ps->update; /* Init confirm counter */ ps->confirm = 0; /* reset teaching and learning flags to 0 */ ps->flags &= PEER_TEACH_RESET; ps->flags &= PEER_LEARN_RESET; /* if current peer is local */ if (ps->peer->local) { /* if table need resyncfrom local and no process assined */ if ((ps->table->flags & SHTABLE_RESYNC_STATEMASK) == SHTABLE_RESYNC_FROMLOCAL && !(ps->table->flags & SHTABLE_F_RESYNC_ASSIGN)) { /* assign local peer for a lesson, consider lesson already requested */ ps->flags |= PEER_F_LEARN_ASSIGN; ps->table->flags |= (SHTABLE_F_RESYNC_ASSIGN|SHTABLE_F_RESYNC_PROCESS); } } else if ((ps->table->flags & SHTABLE_RESYNC_STATEMASK) == SHTABLE_RESYNC_FROMREMOTE && !(ps->table->flags & SHTABLE_F_RESYNC_ASSIGN)) { /* assign peer for a lesson */ ps->flags |= PEER_F_LEARN_ASSIGN; ps->table->flags |= SHTABLE_F_RESYNC_ASSIGN; } /* switch to waiting message state */ appctx->st0 = PEER_SESS_ST_WAITMSG; goto switchstate; } case PEER_SESS_ST_CONNECT: { struct peer_session *ps = (struct peer_session *)appctx->ctx.peers.ptr; /* Send headers */ repl = snprintf(trash.str, trash.size, PEER_SESSION_PROTO_NAME " 1.0\n%s\n%s %d\n%s %lu %d\n", ps->peer->id, localpeer, (int)getpid(), ps->table->table->id, ps->table->table->type, (int)ps->table->table->key_size); if (repl >= trash.size) { appctx->st0 = PEER_SESS_ST_END; goto switchstate; } repl = bi_putblk(si_ic(si), trash.str, repl); if (repl <= 0) { if (repl == -1) goto full; appctx->st0 = PEER_SESS_ST_END; goto switchstate; } /* switch to the waiting statuscode state */ appctx->st0 = PEER_SESS_ST_GETSTATUS; /* fall through */ } case PEER_SESS_ST_GETSTATUS: { struct peer_session *ps = (struct peer_session *)appctx->ctx.peers.ptr; if (si_ic(si)->flags & CF_WRITE_PARTIAL) ps->statuscode = PEER_SESS_SC_CONNECTEDCODE; reql = bo_getline(si_oc(si), trash.str, trash.size); if (reql <= 0) { /* closed or EOL not found */ if (reql == 0) goto out; appctx->st0 = PEER_SESS_ST_END; goto switchstate; } if (trash.str[reql-1] != '\n') { /* Incomplete line, we quit */ appctx->st0 = PEER_SESS_ST_END; goto switchstate; } else if (reql > 1 && (trash.str[reql-2] == '\r')) trash.str[reql-2] = 0; else trash.str[reql-1] = 0; bo_skip(si_oc(si), reql); /* Register status code */ ps->statuscode = atoi(trash.str); /* Awake main task */ task_wakeup(ps->table->sync_task, TASK_WOKEN_MSG); /* If status code is success */ if (ps->statuscode == PEER_SESS_SC_SUCCESSCODE) { /* Init cursors */ ps->teaching_origin = ps->lastpush = ps->lastack = ps->pushack = 0; ps->pushed = ps->update; /* Init confirm counter */ ps->confirm = 0; /* reset teaching and learning flags to 0 */ ps->flags &= PEER_TEACH_RESET; ps->flags &= PEER_LEARN_RESET; /* If current peer is local */ if (ps->peer->local) { /* Init cursors to push a resync */ ps->teaching_origin = ps->pushed = ps->table->table->update; /* flag to start to teach lesson */ ps->flags |= PEER_F_TEACH_PROCESS; } else if ((ps->table->flags & SHTABLE_RESYNC_STATEMASK) == SHTABLE_RESYNC_FROMREMOTE && !(ps->table->flags & SHTABLE_F_RESYNC_ASSIGN)) { /* If peer is remote and resync from remote is needed, and no peer currently assigned */ /* assign peer for a lesson */ ps->flags |= PEER_F_LEARN_ASSIGN; ps->table->flags |= SHTABLE_F_RESYNC_ASSIGN; } } else { /* Status code is not success, abort */ appctx->st0 = PEER_SESS_ST_END; goto switchstate; } appctx->st0 = PEER_SESS_ST_WAITMSG; /* fall through */ } case PEER_SESS_ST_WAITMSG: { struct peer_session *ps = (struct peer_session *)appctx->ctx.peers.ptr; struct stksess *ts, *newts = NULL; char c; int totl = 0; reql = bo_getblk(si_oc(si), (char *)&c, sizeof(c), totl); if (reql <= 0) /* closed or EOL not found */ goto incomplete; totl += reql; if ((c & 0x80) || (c == 'D')) { /* Here we have data message */ unsigned int pushack; int srvid; uint32_t netinteger; /* Compute update remote version */ if (c & 0x80) { pushack = ps->pushack + (unsigned int)(c & 0x7F); } else { reql = bo_getblk(si_oc(si), (char *)&netinteger, sizeof(netinteger), totl); if (reql <= 0) /* closed or EOL not found */ goto incomplete; totl += reql; pushack = ntohl(netinteger); } /* Read key. The string keys are read in two steps, the first step * consists in reading whatever fits into the table directly into * the pre-allocated key. The second step consists in simply * draining all exceeding data. This can happen for example after a * config reload with a smaller key size for the stick table than * what was previously set, or when facing the impossibility to * allocate a new stksess (for example when the table is full with * "nopurge"). */ if (ps->table->table->type == STKTABLE_TYPE_STRING) { unsigned int to_read, to_store; /* read size first */ reql = bo_getblk(si_oc(si), (char *)&netinteger, sizeof(netinteger), totl); if (reql <= 0) /* closed or EOL not found */ goto incomplete; totl += reql; to_store = 0; to_read = ntohl(netinteger); if (to_read + totl > si_ob(si)->size) { /* impossible to read a key this large, abort */ reql = -1; goto incomplete; } newts = stksess_new(ps->table->table, NULL); if (newts) to_store = MIN(to_read, ps->table->table->key_size - 1); /* we read up to two blocks, the first one goes into the key, * the rest is drained into the trash. */ if (to_store) { reql = bo_getblk(si_oc(si), (char *)newts->key.key, to_store, totl); if (reql <= 0) /* closed or incomplete */ goto incomplete; newts->key.key[reql] = 0; totl += reql; to_read -= reql; } if (to_read) { reql = bo_getblk(si_oc(si), trash.str, to_read, totl); if (reql <= 0) /* closed or incomplete */ goto incomplete; totl += reql; } } else if (ps->table->table->type == STKTABLE_TYPE_INTEGER) { reql = bo_getblk(si_oc(si), (char *)&netinteger, sizeof(netinteger), totl); if (reql <= 0) /* closed or EOL not found */ goto incomplete; newts = stksess_new(ps->table->table, NULL); if (newts) { netinteger = ntohl(netinteger); memcpy(newts->key.key, &netinteger, sizeof(netinteger)); } totl += reql; } else { /* type ip or binary */ newts = stksess_new(ps->table->table, NULL); reql = bo_getblk(si_oc(si), newts ? (char *)newts->key.key : trash.str, ps->table->table->key_size, totl); if (reql <= 0) /* closed or EOL not found */ goto incomplete; totl += reql; } /* read server id */ reql = bo_getblk(si_oc(si), (char *)&netinteger, sizeof(netinteger), totl); if (reql <= 0) /* closed or EOL not found */ goto incomplete; totl += reql; srvid = ntohl(netinteger); /* update entry */ if (newts) { /* lookup for existing entry */ ts = stktable_lookup(ps->table->table, newts); if (ts) { /* the entry already exist, we can free ours */ stktable_touch(ps->table->table, ts, 0); stksess_free(ps->table->table, newts); newts = NULL; } else { struct eb32_node *eb; /* create new entry */ ts = stktable_store(ps->table->table, newts, 0); newts = NULL; /* don't reuse it */ ts->upd.key= (++ps->table->table->update)+(2^31); eb = eb32_insert(&ps->table->table->updates, &ts->upd); if (eb != &ts->upd) { eb32_delete(eb); eb32_insert(&ps->table->table->updates, &ts->upd); } } /* update entry */ if (srvid && stktable_data_ptr(ps->table->table, ts, STKTABLE_DT_SERVER_ID)) stktable_data_cast(stktable_data_ptr(ps->table->table, ts, STKTABLE_DT_SERVER_ID), server_id) = srvid; ps->pushack = pushack; } } else if (c == 'R') { /* Reset message: remote need resync */ /* reinit counters for a resync */ ps->lastpush = 0; ps->teaching_origin = ps->pushed = ps->table->table->update; /* reset teaching flags to 0 */ ps->flags &= PEER_TEACH_RESET; /* flag to start to teach lesson */ ps->flags |= PEER_F_TEACH_PROCESS; } else if (c == 'F') { /* Finish message, all known updates have been pushed by remote */ /* and remote is up to date */ /* If resync is in progress with remote peer */ if (ps->flags & PEER_F_LEARN_ASSIGN) { /* unassign current peer for learning */ ps->flags &= ~PEER_F_LEARN_ASSIGN; ps->table->flags &= ~(SHTABLE_F_RESYNC_ASSIGN|SHTABLE_F_RESYNC_PROCESS); /* Consider table is now up2date, resync resync no more needed from local neither remote */ ps->table->flags |= (SHTABLE_F_RESYNC_LOCAL|SHTABLE_F_RESYNC_REMOTE); } /* Increase confirm counter to launch a confirm message */ ps->confirm++; } else if (c == 'c') { /* confirm message, remote peer is now up to date with us */ /* If stopping state */ if (stopping) { /* Close session, push resync no more needed */ ps->flags |= PEER_F_TEACH_COMPLETE; appctx->st0 = PEER_SESS_ST_END; goto switchstate; } /* reset teaching flags to 0 */ ps->flags &= PEER_TEACH_RESET; } else if (c == 'C') { /* Continue message, all known updates have been pushed by remote */ /* but remote is not up to date */ /* If resync is in progress with current peer */ if (ps->flags & PEER_F_LEARN_ASSIGN) { /* unassign current peer */ ps->flags &= ~PEER_F_LEARN_ASSIGN; ps->table->flags &= ~(SHTABLE_F_RESYNC_ASSIGN|SHTABLE_F_RESYNC_PROCESS); /* flag current peer is not up 2 date to try from an other */ ps->flags |= PEER_F_LEARN_NOTUP2DATE; /* reschedule a resync */ ps->table->resync_timeout = tick_add(now_ms, MS_TO_TICKS(5000)); task_wakeup(ps->table->sync_task, TASK_WOKEN_MSG); } ps->confirm++; } else if (c == 'A') { /* ack message */ uint32_t netinteger; reql = bo_getblk(si_oc(si), (char *)&netinteger, sizeof(netinteger), totl); if (reql <= 0) /* closed or EOL not found */ goto incomplete; totl += reql; /* Consider remote is up to date with "acked" version */ ps->update = ntohl(netinteger); } else { /* Unknown message */ appctx->st0 = PEER_SESS_ST_END; goto switchstate; } /* skip consumed message */ bo_skip(si_oc(si), totl); /* loop on that state to peek next message */ goto switchstate; incomplete: /* we get here when a bo_getblk() returns <= 0 in reql */ /* first, we may have to release newts */ if (newts) { stksess_free(ps->table->table, newts); newts = NULL; } if (reql < 0) { /* there was an error */ appctx->st0 = PEER_SESS_ST_END; goto switchstate; } /* Nothing to read, now we start to write */ /* Confirm finished or partial messages */ while (ps->confirm) { /* There is a confirm messages to send */ repl = bi_putchr(si_ic(si), 'c'); if (repl <= 0) { /* no more write possible */ if (repl == -1) goto full; appctx->st0 = PEER_SESS_ST_END; goto switchstate; } ps->confirm--; } /* Need to request a resync */ if ((ps->flags & PEER_F_LEARN_ASSIGN) && (ps->table->flags & SHTABLE_F_RESYNC_ASSIGN) && !(ps->table->flags & SHTABLE_F_RESYNC_PROCESS)) { /* Current peer was elected to request a resync */ repl = bi_putchr(si_ic(si), 'R'); if (repl <= 0) { /* no more write possible */ if (repl == -1) goto full; appctx->st0 = PEER_SESS_ST_END; goto switchstate; } ps->table->flags |= SHTABLE_F_RESYNC_PROCESS; } /* It remains some updates to ack */ if (ps->pushack != ps->lastack) { uint32_t netinteger; trash.str[0] = 'A'; netinteger = htonl(ps->pushack); memcpy(&trash.str[1], &netinteger, sizeof(netinteger)); repl = bi_putblk(si_ic(si), trash.str, 1+sizeof(netinteger)); if (repl <= 0) { /* no more write possible */ if (repl == -1) goto full; appctx->st0 = PEER_SESS_ST_END; goto switchstate; } ps->lastack = ps->pushack; } if (ps->flags & PEER_F_TEACH_PROCESS) { /* current peer was requested for a lesson */ if (!(ps->flags & PEER_F_TEACH_STAGE1)) { /* lesson stage 1 not complete */ struct eb32_node *eb; eb = eb32_lookup_ge(&ps->table->table->updates, ps->pushed+1); while (1) { int msglen; struct stksess *ts; if (!eb) { /* flag lesson stage1 complete */ ps->flags |= PEER_F_TEACH_STAGE1; eb = eb32_first(&ps->table->table->updates); if (eb) ps->pushed = eb->key - 1; break; } ts = eb32_entry(eb, struct stksess, upd); msglen = peer_prepare_datamsg(ts, ps, trash.str, trash.size); if (msglen) { /* message to buffer */ repl = bi_putblk(si_ic(si), trash.str, msglen); if (repl <= 0) { /* no more write possible */ if (repl == -1) goto full; appctx->st0 = PEER_SESS_ST_END; goto switchstate; } ps->lastpush = ps->pushed = ts->upd.key; } eb = eb32_next(eb); } } /* !TEACH_STAGE1 */ if (!(ps->flags & PEER_F_TEACH_STAGE2)) { /* lesson stage 2 not complete */ struct eb32_node *eb; eb = eb32_lookup_ge(&ps->table->table->updates, ps->pushed+1); while (1) { int msglen; struct stksess *ts; if (!eb || eb->key > ps->teaching_origin) { /* flag lesson stage1 complete */ ps->flags |= PEER_F_TEACH_STAGE2; ps->pushed = ps->teaching_origin; break; } ts = eb32_entry(eb, struct stksess, upd); msglen = peer_prepare_datamsg(ts, ps, trash.str, trash.size); if (msglen) { /* message to buffer */ repl = bi_putblk(si_ic(si), trash.str, msglen); if (repl <= 0) { /* no more write possible */ if (repl == -1) goto full; appctx->st0 = PEER_SESS_ST_END; goto switchstate; } ps->lastpush = ps->pushed = ts->upd.key; } eb = eb32_next(eb); } } /* !TEACH_STAGE2 */ if (!(ps->flags & PEER_F_TEACH_FINISHED)) { /* process final lesson message */ repl = bi_putchr(si_ic(si), ((ps->table->flags & SHTABLE_RESYNC_STATEMASK) == SHTABLE_RESYNC_FINISHED) ? 'F' : 'C'); if (repl <= 0) { /* no more write possible */ if (repl == -1) goto full; appctx->st0 = PEER_SESS_ST_END; goto switchstate; } /* flag finished message sent */ ps->flags |= PEER_F_TEACH_FINISHED; } /* !TEACH_FINISHED */ } /* TEACH_PROCESS */ if (!(ps->flags & PEER_F_LEARN_ASSIGN) && (int)(ps->pushed - ps->table->table->localupdate) < 0) { /* Push local updates, only if no learning in progress (to avoid ping-pong effects) */ struct eb32_node *eb; eb = eb32_lookup_ge(&ps->table->table->updates, ps->pushed+1); while (1) { int msglen; struct stksess *ts; /* push local updates */ if (!eb) { eb = eb32_first(&ps->table->table->updates); if (!eb || ((int)(eb->key - ps->pushed) <= 0)) { ps->pushed = ps->table->table->localupdate; break; } } if ((int)(eb->key - ps->table->table->localupdate) > 0) { ps->pushed = ps->table->table->localupdate; break; } ts = eb32_entry(eb, struct stksess, upd); msglen = peer_prepare_datamsg(ts, ps, trash.str, trash.size); if (msglen) { /* message to buffer */ repl = bi_putblk(si_ic(si), trash.str, msglen); if (repl <= 0) { /* no more write possible */ if (repl == -1) goto full; appctx->st0 = PEER_SESS_ST_END; goto switchstate; } ps->lastpush = ps->pushed = ts->upd.key; } eb = eb32_next(eb); } } /* ! LEARN_ASSIGN */ /* noting more to do */ goto out; } case PEER_SESS_ST_EXIT: repl = snprintf(trash.str, trash.size, "%d\n", appctx->st1); if (bi_putblk(si_ic(si), trash.str, repl) == -1) goto full; appctx->st0 = PEER_SESS_ST_END; /* fall through */ case PEER_SESS_ST_END: { si_shutw(si); si_shutr(si); si_ic(si)->flags |= CF_READ_NULL; goto out; } } } out: si_oc(si)->flags |= CF_READ_DONTWAIT; return; full: si_applet_cant_put(si); goto out; }