u32_t sio_read( sio_fd_t fd, u8_t * buf, u32_t size ) { u32_t ch_left = size; u32_t ch_received = 0; volatile serdev_t *dev = fd; if( dev->ready ) { dev->abort = 0; while( ch_left && !dev->abort ) { vPortEnterCritical( ); while( ( dev->rx_buf_cnt > 0 ) && ( ch_left > 0 ) ) { /* Fetch character from the ring buffer. */ *buf++ = dev->rx_buf[dev->rx_buf_rdpos]; dev->rx_buf_rdpos = ( dev->rx_buf_rdpos + 1 ) % DEFAULT_RX_BUFSIZE; dev->rx_buf_cnt--; /* Count character received and left for read. */ ch_left--; ch_received++; } vPortExitCritical( ); /* If we want more data block on the semaphore and wait until * something happens. */ if( ch_left ) { if( xSemaphoreTake( dev->rx_sem, MS_TO_TICKS( DEFAULT_READTIMEOUT_MS ) ) == pdFALSE ) { /* A timeout. Abort the read and return the characters * received so far. */ dev->abort = 1; } } } } return ch_received; }
/** * \brief Fixup function for the timer values * * Fixup function for the timer values, (called by the * configuration framework) * \param handle not used * \param gname not used * \param name not used * \param val fixed timer value * \return 0 on success, -1 on error */ int timer_fixup(void *handle, str *gname, str *name, void **val) { ticks_t t; t = MS_TO_TICKS((unsigned int)(long)(*val)); /* fix 0 values to 1 tick (minimum possible wait time ) */ if (t == 0) t = 1; /* size fix checks */ IF_IS_TIMER_NAME(fr_timeout, "fr_timer") else IF_IS_TIMER_NAME(fr_inv_timeout, "fr_inv_timer") else IF_IS_TIMER_NAME(end_of_life, "max_inv_lifetime") else IF_IS_TIMER_NAME(end_of_life, "max_noninv_lifetime") *val = (void *)(long)t; return 0; error: return -1; }
/** * \brief fix timer values to ticks */ int tm_init_timers(void) { default_tm_cfg.fr_timeout=MS_TO_TICKS(default_tm_cfg.fr_timeout); default_tm_cfg.fr_inv_timeout=MS_TO_TICKS(default_tm_cfg.fr_inv_timeout); default_tm_cfg.wait_timeout=MS_TO_TICKS(default_tm_cfg.wait_timeout); default_tm_cfg.delete_timeout=MS_TO_TICKS(default_tm_cfg.delete_timeout); default_tm_cfg.tm_max_inv_lifetime=MS_TO_TICKS(default_tm_cfg.tm_max_inv_lifetime); default_tm_cfg.tm_max_noninv_lifetime=MS_TO_TICKS(default_tm_cfg.tm_max_noninv_lifetime); /* fix 0 values to 1 tick (minimum possible wait time ) */ if (default_tm_cfg.fr_timeout==0) default_tm_cfg.fr_timeout=1; if (default_tm_cfg.fr_inv_timeout==0) default_tm_cfg.fr_inv_timeout=1; if (default_tm_cfg.wait_timeout==0) default_tm_cfg.wait_timeout=1; if (default_tm_cfg.delete_timeout==0) default_tm_cfg.delete_timeout=1; if (default_tm_cfg.rt_t2_timeout_ms==0) default_tm_cfg.rt_t2_timeout_ms=1; if (default_tm_cfg.rt_t1_timeout_ms==0) default_tm_cfg.rt_t1_timeout_ms=1; if (default_tm_cfg.tm_max_inv_lifetime==0) default_tm_cfg.tm_max_inv_lifetime=1; if (default_tm_cfg.tm_max_noninv_lifetime==0) default_tm_cfg.tm_max_noninv_lifetime=1; /* size fit checks */ SIZE_FIT_CHECK(fr_timeout, default_tm_cfg.fr_timeout, "fr_timer"); SIZE_FIT_CHECK(fr_inv_timeout, default_tm_cfg.fr_inv_timeout, "fr_inv_timer"); #ifdef TM_DIFF_RT_TIMEOUT SIZE_FIT_CHECK(rt_t1_timeout_ms, default_tm_cfg.rt_t1_timeout_ms, "retr_timer1"); SIZE_FIT_CHECK(rt_t2_timeout_ms, default_tm_cfg.rt_t2_timeout_ms, "retr_timer2"); #endif SIZE_FIT_CHECK(end_of_life, default_tm_cfg.tm_max_inv_lifetime, "max_inv_lifetime"); SIZE_FIT_CHECK(end_of_life, default_tm_cfg.tm_max_noninv_lifetime, "max_noninv_lifetime"); memset(&user_fr_timeout, 0, sizeof(user_fr_timeout)); memset(&user_fr_inv_timeout, 0, sizeof(user_fr_inv_timeout)); #ifdef TM_DIFF_RT_TIMEOUT memset(&user_rt_t1_timeout_ms, 0, sizeof(user_rt_t1_timeout_ms)); memset(&user_rt_t2_timeout_ms, 0, sizeof(user_rt_t2_timeout_ms)); #endif memset(&user_inv_max_lifetime, 0, sizeof(user_inv_max_lifetime)); memset(&user_noninv_max_lifetime, 0, sizeof(user_noninv_max_lifetime)); DBG("tm: tm_init_timers: fr=%d fr_inv=%d wait=%d delete=%d t1=%d t2=%d" " max_inv_lifetime=%d max_noninv_lifetime=%d\n", default_tm_cfg.fr_timeout, default_tm_cfg.fr_inv_timeout, default_tm_cfg.wait_timeout, default_tm_cfg.delete_timeout, default_tm_cfg.rt_t1_timeout_ms, default_tm_cfg.rt_t2_timeout_ms, default_tm_cfg.tm_max_inv_lifetime, default_tm_cfg.tm_max_noninv_lifetime); return 0; error: return -1; }
/* * Create a new peer session in assigned state (connect will start automatically) */ static struct session *peer_session_create(struct peer *peer, struct peer_session *ps) { struct listener *l = ((struct proxy *)peer->peers->peers_fe)->listen; struct proxy *p = (struct proxy *)l->frontend; /* attached frontend */ struct session *s; struct http_txn *txn; struct task *t; if ((s = pool_alloc2(pool2_session)) == NULL) { /* disable this proxy for a while */ Alert("out of memory in event_accept().\n"); goto out_close; } LIST_ADDQ(&sessions, &s->list); LIST_INIT(&s->back_refs); s->flags = SN_ASSIGNED|SN_ADDR_SET; s->term_trace = 0; /* if this session comes from a known monitoring system, we want to ignore * it as soon as possible, which means closing it immediately for TCP. */ if ((t = task_new()) == NULL) { /* disable this proxy for a while */ Alert("out of memory in event_accept().\n"); goto out_free_session; } ps->reconnect = tick_add(now_ms, MS_TO_TICKS(5000)); ps->statuscode = PEER_SESSION_CONNECTCODE; t->process = l->handler; t->context = s; t->nice = l->nice; memcpy(&s->si[1].conn.addr.to, &peer->addr, sizeof(s->si[1].conn.addr.to)); s->task = t; s->listener = l; /* Note: initially, the session's backend points to the frontend. * This changes later when switching rules are executed or * when the default backend is assigned. */ s->be = s->fe = p; s->req = s->rep = NULL; /* will be allocated later */ s->si[0].conn.t.sock.fd = -1; s->si[0].conn.flags = CO_FL_NONE; s->si[0].owner = t; s->si[0].state = s->si[0].prev_state = SI_ST_EST; s->si[0].err_type = SI_ET_NONE; s->si[0].err_loc = NULL; s->si[0].release = NULL; s->si[0].send_proxy_ofs = 0; set_target_client(&s->si[0].conn.target, l); s->si[0].exp = TICK_ETERNITY; s->si[0].flags = SI_FL_NONE; if (s->fe->options2 & PR_O2_INDEPSTR) s->si[0].flags |= SI_FL_INDEP_STR; stream_int_register_handler(&s->si[0], &peer_applet); s->si[0].applet.st0 = PEER_SESSION_CONNECT; s->si[0].conn.data_ctx = (void *)ps; s->si[1].conn.t.sock.fd = -1; /* just to help with debugging */ s->si[1].conn.flags = CO_FL_NONE; s->si[1].owner = t; s->si[1].state = s->si[1].prev_state = SI_ST_ASS; s->si[1].conn_retries = p->conn_retries; s->si[1].err_type = SI_ET_NONE; s->si[1].err_loc = NULL; s->si[1].release = NULL; s->si[1].send_proxy_ofs = 0; set_target_proxy(&s->si[1].conn.target, s->be); si_prepare_conn(&s->si[1], peer->proto, peer->data); s->si[1].exp = TICK_ETERNITY; s->si[1].flags = SI_FL_NONE; if (s->be->options2 & PR_O2_INDEPSTR) s->si[1].flags |= SI_FL_INDEP_STR; session_init_srv_conn(s); set_target_proxy(&s->target, s->be); s->pend_pos = NULL; /* init store persistence */ s->store_count = 0; s->stkctr1_entry = NULL; s->stkctr2_entry = NULL; /* FIXME: the logs are horribly complicated now, because they are * defined in <p>, <p>, and later <be> and <be>. */ s->logs.logwait = 0; s->do_log = NULL; /* default error reporting function, may be changed by analysers */ s->srv_error = default_srv_error; s->uniq_id = 0; s->unique_id = NULL; txn = &s->txn; /* Those variables will be checked and freed if non-NULL in * session.c:session_free(). It is important that they are * properly initialized. */ txn->sessid = NULL; txn->srv_cookie = NULL; txn->cli_cookie = NULL; txn->uri = NULL; txn->req.cap = NULL; txn->rsp.cap = NULL; txn->hdr_idx.v = NULL; txn->hdr_idx.size = txn->hdr_idx.used = 0; if ((s->req = pool_alloc2(pool2_channel)) == NULL) goto out_fail_req; /* no memory */ s->req->buf.size = global.tune.bufsize; channel_init(s->req); s->req->prod = &s->si[0]; s->req->cons = &s->si[1]; s->si[0].ib = s->si[1].ob = s->req; s->req->flags |= CF_READ_ATTACHED; /* the producer is already connected */ /* activate default analysers enabled for this listener */ s->req->analysers = l->analysers; /* note: this should not happen anymore since there's always at least the switching rules */ if (!s->req->analysers) { channel_auto_connect(s->req);/* don't wait to establish connection */ channel_auto_close(s->req);/* let the producer forward close requests */ } s->req->rto = s->fe->timeout.client; s->req->wto = s->be->timeout.server; if ((s->rep = pool_alloc2(pool2_channel)) == NULL) goto out_fail_rep; /* no memory */ s->rep->buf.size = global.tune.bufsize; channel_init(s->rep); s->rep->prod = &s->si[1]; s->rep->cons = &s->si[0]; s->si[0].ob = s->si[1].ib = s->rep; s->rep->rto = s->be->timeout.server; s->rep->wto = s->fe->timeout.client; s->req->rex = TICK_ETERNITY; s->req->wex = TICK_ETERNITY; s->req->analyse_exp = TICK_ETERNITY; s->rep->rex = TICK_ETERNITY; s->rep->wex = TICK_ETERNITY; s->rep->analyse_exp = TICK_ETERNITY; t->expire = TICK_ETERNITY; s->rep->flags |= CF_READ_DONTWAIT; /* it is important not to call the wakeup function directly but to * pass through task_wakeup(), because this one knows how to apply * priorities to tasks. */ task_wakeup(t, TASK_WOKEN_INIT); l->nbconn++; /* warning! right now, it's up to the handler to decrease this */ p->feconn++;/* beconn will be increased later */ jobs++; if (!(s->listener->options & LI_O_UNLIMITED)) actconn++; totalconn++; return s; /* Error unrolling */ out_fail_rep: pool_free2(pool2_channel, s->req); out_fail_req: task_free(t); out_free_session: LIST_DEL(&s->list); pool_free2(pool2_session, s); out_close: return s; }
/* initialize ratelimit module */ static int mod_init(void) { if(rpc_register_array(rpc_methods)!=0) { LM_ERR("failed to register RPC commands\n"); return -1; } if(register_mi_mod(exports.name, mi_cmds)!=0) { LM_ERR("failed to register MI commands\n"); return -1; } if(pl_hash_size<=0) { LM_ERR("invalid hash size parameter: %d\n", pl_hash_size); return -1; } if(pl_init_htable(1<<pl_hash_size)<0) { LM_ERR("could not allocate pipes htable\n"); return -1; } if(pl_init_db()<0) { LM_ERR("could not load pipes description\n"); return -1; } /* register timer to reset counters */ if ((pl_timer = timer_alloc()) == NULL) { LM_ERR("could not allocate timer\n"); return -1; } timer_init(pl_timer, pl_timer_handle, 0, F_TIMER_FAST); timer_add(pl_timer, MS_TO_TICKS(1000)); /* Start it after 1000ms */ /* bind the SL API */ if (sl_load_api(&slb)!=0) { LM_ERR("cannot bind to SL API\n"); return -1; } network_load_value = shm_malloc(sizeof(int)); if (network_load_value==NULL) { LM_ERR("oom for network_load_value\n"); return -1; } load_value = shm_malloc(sizeof(double)); if (load_value==NULL) { LM_ERR("oom for load_value\n"); return -1; } load_source = shm_malloc(sizeof(int)); if (load_source==NULL) { LM_ERR("oom for load_source\n"); return -1; } pid_kp = shm_malloc(sizeof(double)); if (pid_kp==NULL) { LM_ERR("oom for pid_kp\n"); return -1; } pid_ki = shm_malloc(sizeof(double)); if (pid_ki==NULL) { LM_ERR("oom for pid_ki\n"); return -1; } pid_kd = shm_malloc(sizeof(double)); if (pid_kd==NULL) { LM_ERR("oom for pid_kd\n"); return -1; } _pl_pid_setpoint = shm_malloc(sizeof(double)); if (_pl_pid_setpoint==NULL) { LM_ERR("oom for pid_setpoint\n"); return -1; } drop_rate = shm_malloc(sizeof(int)); if (drop_rate==NULL) { LM_ERR("oom for drop_rate\n"); return -1; } *network_load_value = 0; *load_value = 0.0; *load_source = load_source_mp; *pid_kp = 0.0; *pid_ki = -25.0; *pid_kd = 0.0; *_pl_pid_setpoint = 0.01 * (double)_pl_cfg_setpoint; *drop_rate = 0; return 0; }
/* This function parses a "timeout" statement in a proxy section. It returns * -1 if there is any error, 1 for a warning, otherwise zero. If it does not * return zero, it will write an error or warning message into a preallocated * buffer returned at <err>. The trailing is not be written. The function must * be called with <args> pointing to the first command line word, with <proxy> * pointing to the proxy being parsed, and <defpx> to the default proxy or NULL. * As a special case for compatibility with older configs, it also accepts * "{cli|srv|con}timeout" in args[0]. */ static int proxy_parse_timeout(char **args, int section, struct proxy *proxy, struct proxy *defpx, const char *file, int line, char **err) { unsigned timeout; int retval, cap; const char *res, *name; int *tv = NULL; int *td = NULL; int warn = 0; retval = 0; /* simply skip "timeout" but remain compatible with old form */ if (strcmp(args[0], "timeout") == 0) args++; name = args[0]; if (!strcmp(args[0], "client") || (!strcmp(args[0], "clitimeout") && (warn = WARN_CLITO_DEPRECATED))) { name = "client"; tv = &proxy->timeout.client; td = &defpx->timeout.client; cap = PR_CAP_FE; } else if (!strcmp(args[0], "tarpit")) { tv = &proxy->timeout.tarpit; td = &defpx->timeout.tarpit; cap = PR_CAP_FE | PR_CAP_BE; } else if (!strcmp(args[0], "http-keep-alive")) { tv = &proxy->timeout.httpka; td = &defpx->timeout.httpka; cap = PR_CAP_FE | PR_CAP_BE; } else if (!strcmp(args[0], "http-request")) { tv = &proxy->timeout.httpreq; td = &defpx->timeout.httpreq; cap = PR_CAP_FE | PR_CAP_BE; } else if (!strcmp(args[0], "server") || (!strcmp(args[0], "srvtimeout") && (warn = WARN_SRVTO_DEPRECATED))) { name = "server"; tv = &proxy->timeout.server; td = &defpx->timeout.server; cap = PR_CAP_BE; } else if (!strcmp(args[0], "connect") || (!strcmp(args[0], "contimeout") && (warn = WARN_CONTO_DEPRECATED))) { name = "connect"; tv = &proxy->timeout.connect; td = &defpx->timeout.connect; cap = PR_CAP_BE; } else if (!strcmp(args[0], "check")) { tv = &proxy->timeout.check; td = &defpx->timeout.check; cap = PR_CAP_BE; } else if (!strcmp(args[0], "queue")) { tv = &proxy->timeout.queue; td = &defpx->timeout.queue; cap = PR_CAP_BE; } else if (!strcmp(args[0], "tunnel")) { tv = &proxy->timeout.tunnel; td = &defpx->timeout.tunnel; cap = PR_CAP_BE; } else if (!strcmp(args[0], "client-fin")) { tv = &proxy->timeout.clientfin; td = &defpx->timeout.clientfin; cap = PR_CAP_FE; } else if (!strcmp(args[0], "server-fin")) { tv = &proxy->timeout.serverfin; td = &defpx->timeout.serverfin; cap = PR_CAP_BE; } else { memprintf(err, "'timeout' supports 'client', 'server', 'connect', 'check', " "'queue', 'http-keep-alive', 'http-request', 'tunnel', 'tarpit', " "'client-fin' and 'server-fin' (got '%s')", args[0]); return -1; } if (*args[1] == 0) { memprintf(err, "'timeout %s' expects an integer value (in milliseconds)", name); return -1; } res = parse_time_err(args[1], &timeout, TIME_UNIT_MS); if (res) { memprintf(err, "unexpected character '%c' in 'timeout %s'", *res, name); return -1; } if (!(proxy->cap & cap)) { memprintf(err, "'timeout %s' will be ignored because %s '%s' has no %s capability", name, proxy_type_str(proxy), proxy->id, (cap & PR_CAP_BE) ? "backend" : "frontend"); retval = 1; } else if (defpx && *tv != *td) { memprintf(err, "overwriting 'timeout %s' which was already specified", name); retval = 1; } else if (warn) { if (!already_warned(warn)) { memprintf(err, "the '%s' directive is now deprecated in favor of 'timeout %s', and will not be supported in future versions.", args[0], name); retval = 1; } } if (*args[2] != 0) { memprintf(err, "'timeout %s' : unexpected extra argument '%s' after value '%s'.", name, args[2], args[1]); retval = -1; } *tv = MS_TO_TICKS(timeout); return retval; }
/* initialize ratelimit module */ static int mod_init(void) { int i; if (rpc_register_array(rpc_methods)!=0) { LM_ERR("failed to register RPC commands\n"); return -1; } rl_lock = lock_alloc(); if (! rl_lock) { LM_ERR("oom in lock_alloc()\n"); return -1; } if (lock_init(rl_lock)==0) { LM_ERR("failed to init lock\n"); return -1; } /* register timer to reset counters */ if ((rl_timer = timer_alloc()) == NULL) { LM_ERR("could not allocate timer\n"); return -1; } timer_init(rl_timer, rl_timer_handle, 0, F_TIMER_FAST); timer_add(rl_timer, MS_TO_TICKS(1000*timer_interval)); network_load_value = shm_malloc(sizeof(int)); if (network_load_value==NULL) { LM_ERR("oom for network_load_value\n"); return -1; } load_value = shm_malloc(sizeof(double)); if (load_value==NULL) { LM_ERR("oom for load_value\n"); return -1; } load_source = shm_malloc(sizeof(int)); if (load_source==NULL) { LM_ERR("oom for load_source\n"); return -1; } pid_kp = shm_malloc(sizeof(double)); if (pid_kp==NULL) { LM_ERR("oom for pid_kp\n"); return -1; } pid_ki = shm_malloc(sizeof(double)); if (pid_ki==NULL) { LM_ERR("oom for pid_ki\n"); return -1; } pid_kd = shm_malloc(sizeof(double)); if (pid_kd==NULL) { LM_ERR("oom for pid_kd\n"); return -1; } pid_setpoint = shm_malloc(sizeof(double)); if (pid_setpoint==NULL) { LM_ERR("oom for pid_setpoint\n"); return -1; } drop_rate = shm_malloc(sizeof(int)); if (drop_rate==NULL) { LM_ERR("oom for drop_rate\n"); return -1; } nqueues = shm_malloc(sizeof(int)); if (nqueues==NULL) { LM_ERR("oom for nqueues\n"); return -1; } rl_dbg_str = shm_malloc(sizeof(str)); if (rl_dbg_str==NULL) { LM_ERR("oom for rl_dbg_str\n"); return -1; } *network_load_value = 0; *load_value = 0.0; *load_source = load_source_mp; *pid_kp = 0.0; *pid_ki = -25.0; *pid_kd = 0.0; *pid_setpoint = 0.01 * (double)cfg_setpoint; *drop_rate = 0; *nqueues = nqueues_mp; rl_dbg_str->s = NULL; rl_dbg_str->len = 0; for (i=0; i<MAX_PIPES; i++) { pipes[i].algo = shm_malloc(sizeof(int)); if (pipes[i].algo==NULL) { LM_ERR("oom for pipes[%d].algo\n", i); return -1; } pipes[i].limit = shm_malloc(sizeof(int)); if (pipes[i].limit==NULL) { LM_ERR("oom for pipes[%d].limit\n", i); return -1; } pipes[i].load = shm_malloc(sizeof(int)); if (pipes[i].load==NULL) { LM_ERR("oom for pipes[%d].load\n", i); return -1; } pipes[i].counter = shm_malloc(sizeof(int)); if (pipes[i].counter==NULL) { LM_ERR("oom for pipes[%d].counter\n", i); return -1; } pipes[i].last_counter = shm_malloc(sizeof(int)); if (pipes[i].last_counter==NULL) { LM_ERR("oom for pipes[%d].last_counter\n", i); return -1; } *pipes[i].algo = pipes[i].algo_mp; *pipes[i].limit = pipes[i].limit_mp; *pipes[i].load = 0; *pipes[i].counter = 0; *pipes[i].last_counter = 0; } for (i=0; i<*nqueues; i++) { queues[i].pipe = shm_malloc(sizeof(int)); if (queues[i].pipe==NULL) { LM_ERR("oom for queues[%d].pipe\n", i); return -1; } queues[i].method = shm_malloc(sizeof(str)); if (queues[i].method==NULL) { LM_ERR("oom for queues[%d].method\n", i); return -1; } *queues[i].pipe = queues[i].pipe_mp; if (queues[i].method_mp.s == NULL) { LM_ERR("unexpected NULL method for queues[%d].method_mp\n", i); return -1; } if(str_cpy(queues[i].method, &queues[i].method_mp)) { LM_ERR("oom str_cpy(queues[%d].method\n", i); return -1; } pkg_free(queues[i].method_mp.s); queues[i].method_mp.s = NULL; queues[i].method_mp.len = 0; } return 0; }
/* This function parses a "timeout" statement in a proxy section. It returns * -1 if there is any error, 1 for a warning, otherwise zero. If it does not * return zero, it may write an error message into the <err> buffer, for at * most <errlen> bytes, trailing zero included. The trailing '\n' must not * be written. The function must be called with <args> pointing to the first * command line word, with <proxy> pointing to the proxy being parsed, and * <defpx> to the default proxy or NULL. As a special case for compatibility * with older configs, it also accepts "{cli|srv|con}timeout" in args[0]. */ static int proxy_parse_timeout(char **args, int section, struct proxy *proxy, struct proxy *defpx, char *err, int errlen) { unsigned timeout; int retval, cap; const char *res, *name; int *tv = NULL; int *td = NULL; retval = 0; /* simply skip "timeout" but remain compatible with old form */ if (strcmp(args[0], "timeout") == 0) args++; name = args[0]; if (!strcmp(args[0], "client") || !strcmp(args[0], "clitimeout")) { name = "client"; tv = &proxy->timeout.client; td = &defpx->timeout.client; cap = PR_CAP_FE; } else if (!strcmp(args[0], "tarpit")) { tv = &proxy->timeout.tarpit; td = &defpx->timeout.tarpit; cap = PR_CAP_FE | PR_CAP_BE; } else if (!strcmp(args[0], "http-request")) { tv = &proxy->timeout.httpreq; td = &defpx->timeout.httpreq; cap = PR_CAP_FE; } else if (!strcmp(args[0], "server") || !strcmp(args[0], "srvtimeout")) { name = "server"; tv = &proxy->timeout.server; td = &defpx->timeout.server; cap = PR_CAP_BE; } else if (!strcmp(args[0], "connect") || !strcmp(args[0], "contimeout")) { name = "connect"; tv = &proxy->timeout.connect; td = &defpx->timeout.connect; cap = PR_CAP_BE; } else if (!strcmp(args[0], "check")) { tv = &proxy->timeout.check; td = &defpx->timeout.check; cap = PR_CAP_BE; } else if (!strcmp(args[0], "appsession")) { snprintf(err, errlen, "undocumented 'timeout appsession' may not do what you think and will be removed in next version"); retval = 1; tv = &proxy->timeout.appsession; td = &defpx->timeout.appsession; cap = PR_CAP_BE; } else if (!strcmp(args[0], "queue")) { tv = &proxy->timeout.queue; td = &defpx->timeout.queue; cap = PR_CAP_BE; } else { snprintf(err, errlen, "timeout '%s': must be 'client', 'server', 'connect', 'check', " "'appsession', 'queue', 'http-request' or 'tarpit'", args[0]); return -1; } if (*args[1] == 0) { snprintf(err, errlen, "%s timeout expects an integer value (in milliseconds)", name); return -1; } res = parse_time_err(args[1], &timeout, TIME_UNIT_MS); if (res) { snprintf(err, errlen, "unexpected character '%c' in %s timeout", *res, name); return -1; } if (!(proxy->cap & cap)) { snprintf(err, errlen, "%s timeout will be ignored because %s '%s' has no %s capability", name, proxy_type_str(proxy), proxy->id, (cap & PR_CAP_BE) ? "backend" : "frontend"); retval = 1; } else if (defpx && *tv != *td) { snprintf(err, errlen, "overwriting %s timeout which was already specified", name); retval = 1; } *tv = MS_TO_TICKS(timeout); return retval; }
/* the following assumption are made (to avoid deleting/re-adding the timer): * retr_buf->retr_interval < ( 1<<((sizeof(ticks_t)*8-1) ) * if retr_buf->retr_interval==0 => timer disabled * ==(ticks_t) -1 => retr. disabled (fr working) * retr_buf->retr_interval & (1 <<(sizeof(ticks_t)*8-1) => retr. & fr reset * (we never reset only retr, it's either reset both of them or retr * disabled & reset fr). In this case the fr_origin will contain the * "time" of the reset and next retr should occur at * fr->origin+retr_interval (we also assume that we'll never reset retr * to a lower value then the current one) */ ticks_t retr_buf_handler(ticks_t ticks, struct timer_ln *tl, void *p) { struct retr_buf *rbuf; ticks_t fr_remainder; ticks_t retr_remainder; ticks_t retr_interval; unsigned long new_retr_interval_ms; unsigned long crt_retr_interval_ms; struct cell *t; rbuf = (struct retr_buf *)((void *)tl - (void *)(&((struct retr_buf *)0)->timer)); membar_depends(); /* to be on the safe side */ t = rbuf->my_T; #ifdef TIMER_DEBUG LM_DBG("timer retr_buf_handler @%d (%p -> %p -> %p)\n", ticks, tl, rbuf, t); #endif if(unlikely(rbuf->flags & F_RB_DEL_TIMER)) { /* timer marked for deletion */ rbuf->t_active = 0; /* mark it as removed */ /* a membar is not really needed, in the very unlikely case that * another process will see old t_active's value and will try to * delete the timer again, but since timer_del it's safe in this cases * it will be a no-op */ return 0; } /* overflow safe check (should work ok for fr_intervals < max ticks_t/2) */ if((s_ticks_t)(rbuf->fr_expire - ticks) <= 0) { /* final response */ rbuf->t_active = 0; /* mark the timer as removed (both timers disabled) a little race risk, but nothing bad would happen */ rbuf->flags |= F_RB_TIMEOUT; /* WARNING: the next line depends on taking care not to start the * wait timer before finishing with t (if this is not * guaranteed then comment the timer_allow_del() line) */ timer_allow_del(); /* [optional] allow timer_dels, since we're done and there is no race risk */ final_response_handler(rbuf, t); return 0; } else { /* 4 possible states running (t1), t2, paused, disabled */ if((s_ticks_t)(rbuf->retr_expire - ticks) <= 0) { if(rbuf->flags & F_RB_RETR_DISABLED) goto disabled; crt_retr_interval_ms = (unsigned long)p; /* get the current interval from timer param. */ if(unlikely((rbuf->flags & F_RB_T2) || (crt_retr_interval_ms > RT_T2_TIMEOUT_MS(rbuf)))) { retr_interval = MS_TO_TICKS(RT_T2_TIMEOUT_MS(rbuf)); new_retr_interval_ms = RT_T2_TIMEOUT_MS(rbuf); } else { retr_interval = MS_TO_TICKS(crt_retr_interval_ms); new_retr_interval_ms = crt_retr_interval_ms << 1; } #ifdef TIMER_DEBUG LM_DBG("new interval %ld ms / %d ticks" " (max %d ms)\n", new_retr_interval_ms, retr_interval, RT_T2_TIMEOUT_MS(rbuf)); #endif /* we could race with the reply_received code, but the * worst thing that can happen is to delay a reset_to_t2 * for crt_interval and send an extra retr.*/ rbuf->retr_expire = ticks + retr_interval; /* set new interval to -1 on error, or retr_int. on success */ retr_remainder = retransmission_handler(rbuf) | retr_interval; /* store the next retr. interval in ms inside the timer struct, * in the data member */ tl->data = (void *)(new_retr_interval_ms); } else { retr_remainder = rbuf->retr_expire - ticks; LM_DBG("retr - nothing to do, expire in %d\n", retr_remainder); } } /* skip: */ /* return minimum of the next retransmission handler and the * final response (side benefit: it properly cancels timer if ret==0 and * sleeps for fr_remainder if retr. is canceled [==(ticks_t)-1]) */ fr_remainder = rbuf->fr_expire - ticks; /* to be more precise use get_ticks_raw() instead of ticks (but make sure that crt. ticks < fr_expire */ #ifdef TIMER_DEBUG LM_DBG("timer retr_buf_handler @%d (%p ->%p->%p) exiting min (%d, %d)\n", ticks, tl, rbuf, t, retr_remainder, fr_remainder); #endif #ifdef EXTRA_DEBUG if(retr_remainder == 0 || fr_remainder == 0) { LM_BUG("0 remainder => disabling timer!: " "retr_remainder=%d, fr_remainder=%d\n", retr_remainder, fr_remainder); } #endif if(retr_remainder < fr_remainder) return retr_remainder; else { /* hack to switch to the slow timer */ #ifdef TM_FAST_RETR_TIMER tl->flags &= ~F_TIMER_FAST; #endif return fr_remainder; } disabled: return rbuf->fr_expire - ticks; }
/* * Create a new peer session in assigned state (connect will start automatically) */ static struct stream *peer_session_create(struct peer *peer, struct peer_session *ps) { struct listener *l = LIST_NEXT(&peer->peers->peers_fe->conf.listeners, struct listener *, by_fe); struct proxy *p = (struct proxy *)l->frontend; /* attached frontend */ struct appctx *appctx; struct session *sess; struct stream *s; struct task *t; struct connection *conn; ps->reconnect = tick_add(now_ms, MS_TO_TICKS(5000)); ps->statuscode = PEER_SESS_SC_CONNECTCODE; s = NULL; appctx = appctx_new(&peer_applet); if (!appctx) goto out_close; appctx->st0 = PEER_SESS_ST_CONNECT; appctx->ctx.peers.ptr = (void *)ps; sess = session_new(p, l, &appctx->obj_type); if (!sess) { Alert("out of memory in peer_session_create().\n"); goto out_free_appctx; } if ((t = task_new()) == NULL) { Alert("out of memory in peer_session_create().\n"); goto out_free_sess; } t->nice = l->nice; if ((s = stream_new(sess, t, &appctx->obj_type)) == NULL) { Alert("Failed to initialize stream in peer_session_create().\n"); goto out_free_task; } /* The tasks below are normally what is supposed to be done by * fe->accept(). */ s->flags = SF_ASSIGNED|SF_ADDR_SET; /* applet is waiting for data */ si_applet_cant_get(&s->si[0]); appctx_wakeup(appctx); /* initiate an outgoing connection */ si_set_state(&s->si[1], SI_ST_ASS); /* automatically prepare the stream interface to connect to the * pre-initialized connection in si->conn. */ if (unlikely((conn = conn_new()) == NULL)) goto out_free_strm; conn_prepare(conn, peer->proto, peer->xprt); si_attach_conn(&s->si[1], conn); conn->target = s->target = &s->be->obj_type; memcpy(&conn->addr.to, &peer->addr, sizeof(conn->addr.to)); s->do_log = NULL; s->uniq_id = 0; s->res.flags |= CF_READ_DONTWAIT; l->nbconn++; /* warning! right now, it's up to the handler to decrease this */ p->feconn++;/* beconn will be increased later */ jobs++; if (!(s->sess->listener->options & LI_O_UNLIMITED)) actconn++; totalconn++; ps->appctx = appctx; ps->stream = s; return s; /* Error unrolling */ out_free_strm: LIST_DEL(&s->list); pool_free2(pool2_stream, s); out_free_task: task_free(t); out_free_sess: session_free(sess); out_free_appctx: appctx_free(appctx); out_close: return s; }
void lcd_splashscreen(void) { lcd_timeout = getticks() + MS_TO_TICKS(1000 * 5); lcd_timer = true; }
/* adds a proto ip:port combination to the blacklist * returns 0 on success, -1 on error (blacklist full -- would use more then * blst:_max_mem, or out of shm. mem.) */ inline static int dst_blacklist_add_ip(unsigned char err_flags, unsigned char proto, struct ip_addr* ip, unsigned short port, ticks_t timeout) { int size; struct dst_blst_entry* e; unsigned short hash; ticks_t now; int ret; ret=0; if (ip->af==AF_INET){ err_flags&=~BLST_IS_IPV6; /* make sure the ipv6 flag is reset */ size=sizeof(struct dst_blst_entry); }else{ err_flags|=BLST_IS_IPV6; size=sizeof(struct dst_blst_entry)+12 /* ipv6 addr - 4 */; } now=get_ticks_raw(); hash=dst_blst_hash_no(proto, ip, port); /* check if the entry already exists */ LOCK_BLST(hash); e=_dst_blacklist_lst_find(hash, ip, proto, port, now); if (e){ e->flags|=err_flags; e->expire=now+timeout; /* update the timeout */ }else{ if (unlikely((*blst_mem_used+size) >= cfg_get(core, core_cfg, blst_max_mem))){ #ifdef USE_DST_BLACKLIST_STATS dst_blacklist_stats[process_no].bkl_lru_cnt++; #endif UNLOCK_BLST(hash); /* first try to free some memory (~ 12%), but don't * spend more then 250 ms*/ dst_blacklist_clean_expired(*blst_mem_used/16*14, 0, MS_TO_TICKS(250)); if (unlikely(*blst_mem_used+size >= cfg_get(core, core_cfg, blst_max_mem))){ ret=-1; goto error; } LOCK_BLST(hash); } e=shm_malloc(size); if (e==0){ UNLOCK_BLST(hash); ret=E_OUT_OF_MEM; goto error; } *blst_mem_used+=size; e->flags=err_flags; e->proto=proto; e->port=port; memcpy(e->ip, ip->u.addr, ip->len); e->expire=now+timeout; /* update the timeout */ e->next=0; dst_blacklist_lst_add(&dst_blst_hash[hash].first, e); BLST_HASH_STATS_INC(hash); } UNLOCK_BLST(hash); error: return ret; }
static void loop(void) { streamPush(task1Stream, count); count += 25; delay(&task2, MS_TO_TICKS(4100)); }
static void setup(void) { count = 0; delay(&task2, MS_TO_TICKS(4100)); }
void SleepHandler::doHibernate(uint32_t ms, bool interruptible) { // First, make sure we configure timer2 and get it running as // fast as possible. This makes the total sleep delay as // accurate as possible. // Backup timer2 settings uint8_t timsk2 = TIMSK2; uint8_t tccr2a = TCCR2A; uint8_t tccr2b = TCCR2B; // Disable timer2 interrupts, stop the timer and reset it // to normal mode. It seems that for some reason, switching to // asynchronous mode without these registers cleared can somehow // mess up the OCR2x register values... TIMSK2 = 0; TCCR2B = 0; TCCR2A = 0; // Backup the counter. There is a small race condition here // where a counter interrupt could be missed, but since we're // about to sleep for a while and timer2 probably isn't used by // anything else, that's ok. uint8_t tcnt2 = TCNT2; // Enable asynchronous mode for timer2 (uses external 32kHz // crystal. This might corrupt the settings registers, so we'll // have to (re-)set them all. // TCCR2B. uint8_t assr = ASSR; ASSR |= (1 << AS2); // Outputs disconnected, normal mode TCCR2A = 0;//(1 << WGM21) | (1 << WGM20); // Count all the way up to 0xff TCNT2 = 0; OCR2B = 0xff; // Clear any pending interrupt TIFR2 = (1 << OCF2B); // Start timer, prescaler 1024, meaning a single timer increment // is 1/32s TCCR2B = (1 << CS22) | (1 << CS21) | (1 << CS20); // Now that timer2 is running, perform any additional power // saving preparations // Disable Analag comparator uint8_t acsr = ACSR; ACSR = (1 << ACD); // Disable ADC uint8_t adcsra = ADCSRA; ADCSRA &= ~(1 << ADEN); // TODO: When re-enabling the ADC, wait for the AVDDOK bit // Disable the TX side of UART0. If the 16u2 it connects to is // powered off, it offers a path to ground, so keeping the pin // enabled and high (TTL idle) wastes around 1mA of current. To // detect if the 16u2 is powered on, see if it pulls the RX pin // high (not 100% reliable, but worst case we'll have extra // power usage). uint8_t ucsr0b = UCSR0B; if (UCSR0B & (1 << TXEN0) && !digitalRead(RX0)) UCSR0B &= ~(1 << TXEN0); // Power save mode disables the main clock, but keeps the // external clock for timer2 enabled set_sleep_mode(SLEEP_MODE_PWR_SAVE); sleep_enable(); cli(); // Enable the COMPB interrupt to wake us from sleep TIMSK2 |= (1 << OCIE2B); while (ms >= TIMER_MAX_MS) { // Sleep for a complete timer cycle. If interrupt is // false, this will always wait for a full cycle and // return true. If interrupt is true, this can return // false when another interrupt occurs. if (sleepUntilMatch(interruptible)) { ms -= TIMER_MAX_MS; hibernateMillis += TIMER_MAX_MS; } else { // Another interrupt occurred, bail out ms = 0; } } OCR2B = MS_TO_TICKS(ms); while (ASSR & (1 << OCR2BUB)) /* nothing */; // If there's sleep time left, wait for the final interrupt if (TCNT2 < OCR2B) sleepUntilMatch(interruptible); // Stop the counter. Waiting for this write to be completed has // the side effect that the TCNT2 is also safe to read now // (which contains an invalid value shortly after waking up from // sleep). TCCR2B = 0; while (ASSR & (1 << TCR2BUB)) /* nothing */; hibernateMillis += TICKS_TO_MS(TCNT2); sleep_disable(); // Clear any pending timer2 interrupts before enabling // interrupts globally, just in case TIFR2 = 0xff; sei(); // Restore timer2 settings. ASSR = assr; TCNT2 = tcnt2; TCCR2B = tccr2b; TCCR2A = tccr2a; TIMSK2 = timsk2; // Restore other settings UCSR0B = ucsr0b; ACSR = acsr; ADCSRA = adcsra; while (ADCSRB & (1 << AVDDOK)) /* nothing */; // TODO: Verify precise timings. The datasheet says that the // compare match interrupt happens the timer tick after the // match, but the asynchronous section also suggests (another?) // tick delay. Since a timer tick is fairly long (31ms), this // should be measured. }
/* * Task processing function to manage re-connect and peer session * tasks wakeup on local update. */ static struct task *process_peer_sync(struct task * task) { struct shared_table *st = (struct shared_table *)task->context; struct peer_session *ps; task->expire = TICK_ETERNITY; if (!stopping) { /* Normal case (not soft stop)*/ if (((st->flags & SHTABLE_RESYNC_STATEMASK) == SHTABLE_RESYNC_FROMLOCAL) && (!nb_oldpids || tick_is_expired(st->resync_timeout, now_ms)) && !(st->flags & SHTABLE_F_RESYNC_ASSIGN)) { /* Resync from local peer needed no peer was assigned for the lesson and no old local peer found or resync timeout expire */ /* flag no more resync from local, to try resync from remotes */ st->flags |= SHTABLE_F_RESYNC_LOCAL; /* reschedule a resync */ st->resync_timeout = tick_add(now_ms, MS_TO_TICKS(5000)); } /* For each session */ for (ps = st->sessions; ps; ps = ps->next) { /* For each remote peers */ if (!ps->peer->local) { if (!ps->session) { /* no active session */ if (ps->statuscode == 0 || ps->statuscode == PEER_SESSION_SUCCESSCODE || ((ps->statuscode == PEER_SESSION_CONNECTCODE || ps->statuscode == PEER_SESSION_CONNECTEDCODE) && tick_is_expired(ps->reconnect, now_ms))) { /* connection never tried * or previous session established with success * or previous session failed during connection * and reconnection timer is expired */ /* retry a connect */ ps->session = peer_session_create(ps->peer, ps); } else if (ps->statuscode == PEER_SESSION_CONNECTCODE || ps->statuscode == PEER_SESSION_CONNECTEDCODE) { /* If previous session failed during connection * but reconnection timer is not expired */ /* reschedule task for reconnect */ task->expire = tick_first(task->expire, ps->reconnect); } /* else do nothing */ } /* !ps->session */ else if (ps->statuscode == PEER_SESSION_SUCCESSCODE) { /* current session is active and established */ if (((st->flags & SHTABLE_RESYNC_STATEMASK) == SHTABLE_RESYNC_FROMREMOTE) && !(st->flags & SHTABLE_F_RESYNC_ASSIGN) && !(ps->flags & PEER_F_LEARN_NOTUP2DATE)) { /* Resync from a remote is needed * and no peer was assigned for lesson * and current peer may be up2date */ /* assign peer for the lesson */ ps->flags |= PEER_F_LEARN_ASSIGN; st->flags |= SHTABLE_F_RESYNC_ASSIGN; /* awake peer session task to handle a request of resync */ task_wakeup(ps->session->task, TASK_WOKEN_MSG); } else if ((int)(ps->pushed - ps->table->table->localupdate) < 0) { /* awake peer session task to push local updates */ task_wakeup(ps->session->task, TASK_WOKEN_MSG); } /* else do nothing */ } /* SUCCESSCODE */ } /* !ps->peer->local */ } /* for */ /* Resync from remotes expired: consider resync is finished */ if (((st->flags & SHTABLE_RESYNC_STATEMASK) == SHTABLE_RESYNC_FROMREMOTE) && !(st->flags & SHTABLE_F_RESYNC_ASSIGN) && tick_is_expired(st->resync_timeout, now_ms)) { /* Resync from remote peer needed * no peer was assigned for the lesson * and resync timeout expire */ /* flag no more resync from remote, consider resync is finished */ st->flags |= SHTABLE_F_RESYNC_REMOTE; } if ((st->flags & SHTABLE_RESYNC_STATEMASK) != SHTABLE_RESYNC_FINISHED) { /* Resync not finished*/ /* reschedule task to resync timeout, to ended resync if needed */ task->expire = tick_first(task->expire, st->resync_timeout); } } /* !stopping */ else { /* soft stop case */ if (task->state & TASK_WOKEN_SIGNAL) { /* We've just recieved the signal */ if (!(st->flags & SHTABLE_F_DONOTSTOP)) { /* add DO NOT STOP flag if not present */ jobs++; st->flags |= SHTABLE_F_DONOTSTOP; } /* disconnect all connected peers */ for (ps = st->sessions; ps; ps = ps->next) { if (ps->session) { peer_session_forceshutdown(ps->session); ps->session = NULL; } } } ps = st->local_session; if (ps->flags & PEER_F_TEACH_COMPLETE) { if (st->flags & SHTABLE_F_DONOTSTOP) { /* resync of new process was complete, current process can die now */ jobs--; st->flags &= ~SHTABLE_F_DONOTSTOP; } } else if (!ps->session) { /* If session is not active */ if (ps->statuscode == 0 || ps->statuscode == PEER_SESSION_SUCCESSCODE || ps->statuscode == PEER_SESSION_CONNECTEDCODE || ps->statuscode == PEER_SESSION_TRYAGAIN) { /* connection never tried * or previous session was successfully established * or previous session tcp connect success but init state incomplete * or during previous connect, peer replies a try again statuscode */ /* connect to the peer */ ps->session = peer_session_create(ps->peer, ps); } else { /* Other error cases */ if (st->flags & SHTABLE_F_DONOTSTOP) { /* unable to resync new process, current process can die now */ jobs--; st->flags &= ~SHTABLE_F_DONOTSTOP; } } } else if (ps->statuscode == PEER_SESSION_SUCCESSCODE && (int)(ps->pushed - ps->table->table->localupdate) < 0) { /* current session active and established awake session to push remaining local updates */ task_wakeup(ps->session->task, TASK_WOKEN_MSG); } } /* stopping */ /* Wakeup for re-connect */ return task; }
/* * 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; }
/* * 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; }