haproxy/src/proto_uxst.c

1057 lines
30 KiB
C
Raw Normal View History

/*
* UNIX SOCK_STREAM protocol layer (uxst)
*
* Copyright 2000-2008 Willy Tarreau <w@1wt.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
*/
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <time.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/un.h>
#include <common/compat.h>
#include <common/config.h>
#include <common/debug.h>
#include <common/errors.h>
#include <common/memory.h>
#include <common/mini-clist.h>
#include <common/standard.h>
#include <common/ticks.h>
#include <common/time.h>
#include <common/version.h>
#include <types/global.h>
#include <proto/acl.h>
#include <proto/backend.h>
#include <proto/buffers.h>
#include <proto/dumpstats.h>
#include <proto/fd.h>
#include <proto/log.h>
#include <proto/protocols.h>
#include <proto/proto_uxst.h>
#include <proto/queue.h>
#include <proto/session.h>
#include <proto/stream_interface.h>
#include <proto/stream_sock.h>
#include <proto/task.h>
#ifndef MAXPATHLEN
#define MAXPATHLEN 128
#endif
static int uxst_bind_listeners(struct protocol *proto);
static int uxst_unbind_listeners(struct protocol *proto);
/* Note: must not be declared <const> as its list will be overwritten */
static struct protocol proto_unix = {
.name = "unix_stream",
.sock_domain = PF_UNIX,
.sock_type = SOCK_STREAM,
.sock_prot = 0,
.sock_family = AF_UNIX,
.sock_addrlen = sizeof(struct sockaddr_un),
.l3_addrlen = sizeof(((struct sockaddr_un*)0)->sun_path),/* path len */
.read = &stream_sock_read,
.write = &stream_sock_write,
.bind_all = uxst_bind_listeners,
.unbind_all = uxst_unbind_listeners,
.enable_all = enable_all_listeners,
.disable_all = disable_all_listeners,
.listeners = LIST_HEAD_INIT(proto_unix.listeners),
.nb_listeners = 0,
};
const char unix_sock_usage_msg[] =
"Unknown command. Please enter one of the following commands only :\n"
" show info : report information about the running process\n"
" show stat : report counters for each proxy and server\n"
" show errors : report last request and response errors for each proxy\n"
" show sess : report the list of current sessions\n"
"\n";
const struct chunk unix_sock_usage = {
.str = (char *)&unix_sock_usage_msg,
.len = sizeof(unix_sock_usage_msg)-1
};
/********************************
* 1) low-level socket functions
********************************/
/* This function creates a named PF_UNIX stream socket at address <path>. Note
* that the path cannot be NULL nor empty. <uid> and <gid> different of -1 will
* be used to change the socket owner. If <mode> is not 0, it will be used to
* restrict access to the socket. While it is known not to be portable on every
* OS, it's still useful where it works.
* It returns the assigned file descriptor, or -1 in the event of an error.
*/
static int create_uxst_socket(const char *path, uid_t uid, gid_t gid, mode_t mode)
{
char tempname[MAXPATHLEN];
char backname[MAXPATHLEN];
struct sockaddr_un addr;
int ret, sock;
/* 1. create socket names */
if (!path[0]) {
Alert("Invalid name for a UNIX socket. Aborting.\n");
goto err_return;
}
ret = snprintf(tempname, MAXPATHLEN, "%s.%d.tmp", path, pid);
if (ret < 0 || ret >= MAXPATHLEN) {
Alert("name too long for UNIX socket. Aborting.\n");
goto err_return;
}
ret = snprintf(backname, MAXPATHLEN, "%s.%d.bak", path, pid);
if (ret < 0 || ret >= MAXPATHLEN) {
Alert("name too long for UNIX socket. Aborting.\n");
goto err_return;
}
/* 2. clean existing orphaned entries */
if (unlink(tempname) < 0 && errno != ENOENT) {
Alert("error when trying to unlink previous UNIX socket. Aborting.\n");
goto err_return;
}
if (unlink(backname) < 0 && errno != ENOENT) {
Alert("error when trying to unlink previous UNIX socket. Aborting.\n");
goto err_return;
}
/* 3. backup existing socket */
if (link(path, backname) < 0 && errno != ENOENT) {
Alert("error when trying to preserve previous UNIX socket. Aborting.\n");
goto err_return;
}
/* 4. prepare new socket */
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, tempname, sizeof(addr.sun_path));
addr.sun_path[sizeof(addr.sun_path) - 1] = 0;
sock = socket(PF_UNIX, SOCK_STREAM, 0);
if (sock < 0) {
Alert("cannot create socket for UNIX listener. Aborting.\n");
goto err_unlink_back;
}
if (sock >= global.maxsock) {
Alert("socket(): not enough free sockets for UNIX listener. Raise -n argument. Aborting.\n");
goto err_unlink_temp;
}
if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
Alert("cannot make UNIX socket non-blocking. Aborting.\n");
goto err_unlink_temp;
}
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
/* note that bind() creates the socket <tempname> on the file system */
Alert("cannot bind socket for UNIX listener. Aborting.\n");
goto err_unlink_temp;
}
if (((uid != -1 || gid != -1) && (chown(tempname, uid, gid) == -1)) ||
(mode != 0 && chmod(tempname, mode) == -1)) {
Alert("cannot change UNIX socket ownership. Aborting.\n");
goto err_unlink_temp;
}
if (listen(sock, 0) < 0) {
Alert("cannot listen to socket for UNIX listener. Aborting.\n");
goto err_unlink_temp;
}
/* 5. install.
* Point of no return: we are ready, we'll switch the sockets. We don't
* fear loosing the socket <path> because we have a copy of it in
* backname.
*/
if (rename(tempname, path) < 0) {
Alert("cannot switch final and temporary sockets for UNIX listener. Aborting.\n");
goto err_rename;
}
/* 6. cleanup */
unlink(backname); /* no need to keep this one either */
return sock;
err_rename:
ret = rename(backname, path);
if (ret < 0 && errno == ENOENT)
unlink(path);
err_unlink_temp:
unlink(tempname);
close(sock);
err_unlink_back:
unlink(backname);
err_return:
return -1;
}
/* Tries to destroy the UNIX stream socket <path>. The socket must not be used
* anymore. It practises best effort, and no error is returned.
*/
static void destroy_uxst_socket(const char *path)
{
struct sockaddr_un addr;
int sock, ret;
/* We might have been chrooted, so we may not be able to access the
* socket. In order to avoid bothering the other end, we connect with a
* wrong protocol, namely SOCK_DGRAM. The return code from connect()
* is enough to know if the socket is still live or not. If it's live
* in mode SOCK_STREAM, we get EPROTOTYPE or anything else but not
* ECONNREFUSED. In this case, we do not touch it because it's used
* by some other process.
*/
sock = socket(PF_UNIX, SOCK_DGRAM, 0);
if (sock < 0)
return;
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, path, sizeof(addr.sun_path));
addr.sun_path[sizeof(addr.sun_path) - 1] = 0;
ret = connect(sock, (struct sockaddr *)&addr, sizeof(addr));
if (ret < 0 && errno == ECONNREFUSED) {
/* Connect failed: the socket still exists but is not used
* anymore. Let's remove this socket now.
*/
unlink(path);
}
close(sock);
}
/********************************
* 2) listener-oriented functions
********************************/
/* This function creates the UNIX socket associated to the listener. It changes
* the state from ASSIGNED to LISTEN. The socket is NOT enabled for polling.
* The return value is composed from ERR_NONE, ERR_RETRYABLE and ERR_FATAL.
*/
static int uxst_bind_listener(struct listener *listener)
{
int fd;
if (listener->state != LI_ASSIGNED)
return ERR_NONE; /* already bound */
fd = create_uxst_socket(((struct sockaddr_un *)&listener->addr)->sun_path,
listener->perm.ux.uid,
listener->perm.ux.gid,
listener->perm.ux.mode);
if (fd == -1)
return ERR_FATAL;
/* the socket is now listening */
listener->fd = fd;
listener->state = LI_LISTEN;
/* the function for the accept() event */
fd_insert(fd);
fdtab[fd].cb[DIR_RD].f = listener->accept;
fdtab[fd].cb[DIR_WR].f = NULL; /* never called */
fdtab[fd].cb[DIR_RD].b = fdtab[fd].cb[DIR_WR].b = NULL;
fdtab[fd].owner = listener; /* reference the listener instead of a task */
fdtab[fd].state = FD_STLISTEN;
fdtab[fd].peeraddr = NULL;
fdtab[fd].peerlen = 0;
return ERR_NONE;
}
/* This function closes the UNIX sockets for the specified listener.
* The listener enters the LI_ASSIGNED state. It always returns ERR_NONE.
*/
static int uxst_unbind_listener(struct listener *listener)
{
if (listener->state == LI_READY)
EV_FD_CLR(listener->fd, DIR_RD);
if (listener->state >= LI_LISTEN) {
fd_delete(listener->fd);
listener->state = LI_ASSIGNED;
destroy_uxst_socket(((struct sockaddr_un *)&listener->addr)->sun_path);
}
return ERR_NONE;
}
/* Add a listener to the list of unix stream listeners. The listener's state
* is automatically updated from LI_INIT to LI_ASSIGNED. The number of
* listeners is updated. This is the function to use to add a new listener.
*/
void uxst_add_listener(struct listener *listener)
{
if (listener->state != LI_INIT)
return;
listener->state = LI_ASSIGNED;
listener->proto = &proto_unix;
LIST_ADDQ(&proto_unix.listeners, &listener->proto_list);
proto_unix.nb_listeners++;
}
/********************************
* 3) protocol-oriented functions
********************************/
/* This function creates all UNIX sockets bound to the protocol entry <proto>.
* It is intended to be used as the protocol's bind_all() function.
* The sockets will be registered but not added to any fd_set, in order not to
* loose them across the fork(). A call to uxst_enable_listeners() is needed
* to complete initialization.
*
* The return value is composed from ERR_NONE, ERR_RETRYABLE and ERR_FATAL.
*/
static int uxst_bind_listeners(struct protocol *proto)
{
struct listener *listener;
int err = ERR_NONE;
list_for_each_entry(listener, &proto->listeners, proto_list) {
err |= uxst_bind_listener(listener);
if (err != ERR_NONE)
continue;
}
return err;
}
/* This function stops all listening UNIX sockets bound to the protocol
* <proto>. It does not detaches them from the protocol.
* It always returns ERR_NONE.
*/
static int uxst_unbind_listeners(struct protocol *proto)
{
struct listener *listener;
list_for_each_entry(listener, &proto->listeners, proto_list)
uxst_unbind_listener(listener);
return ERR_NONE;
}
/********************************
* 4) high-level functions
********************************/
/*
* This function is called on a read event from a listen socket, corresponding
* to an accept. It tries to accept as many connections as possible.
* It returns 0. Since we use UNIX sockets on the local system for monitoring
* purposes and other related things, we do not need to output as many messages
* as with TCP which can fall under attack.
*/
int uxst_event_accept(int fd) {
struct listener *l = fdtab[fd].owner;
struct session *s;
struct task *t;
int cfd;
int max_accept;
if (global.nbproc > 1)
max_accept = 8; /* let other processes catch some connections too */
else
max_accept = -1;
while (max_accept--) {
struct sockaddr_storage addr;
socklen_t laddr = sizeof(addr);
if ((cfd = accept(fd, (struct sockaddr *)&addr, &laddr)) == -1) {
switch (errno) {
case EAGAIN:
case EINTR:
case ECONNABORTED:
return 0; /* nothing more to accept */
case ENFILE:
/* Process reached system FD limit. Check system tunables. */
return 0;
case EMFILE:
/* Process reached process FD limit. Check 'ulimit-n'. */
return 0;
case ENOBUFS:
case ENOMEM:
/* Process reached system memory limit. Check system tunables. */
return 0;
default:
return 0;
}
}
if (l->nbconn >= l->maxconn || actconn >= global.maxconn) {
/* too many connections, we shoot this one and return.
* FIXME: it would be better to simply switch the listener's
* state to LI_FULL and disable the FD. We could re-enable
* it upon fd_delete(), but this requires all protocols to
* be switched.
*/
goto out_close;
}
if ((s = pool_alloc2(pool2_session)) == NULL) {
Alert("out of memory in uxst_event_accept().\n");
goto out_close;
}
LIST_ADDQ(&sessions, &s->list);
LIST_INIT(&s->back_refs);
[MAJOR] proto_uxst rework -> SNMP support Currently there is a ~16KB limit for a data size passed via unix socket. It is caused by a trivial bug ttat is going to fixed soon, however in most cases there is no need to dump a full stats. This patch makes possible to select a scope of dumped data by extending current "show stat" to "show stat [<iid> <type> <sid>]": - iid is a proxy id, -1 to dump all proxies - type selects type of dumpable objects: 1 for frontend, 2 for backend, 4 for server, -1 for all types. Values can be ORed, for example: 1+2=3 -> frontend+backend. 1+2+4=7 -> frontend+backend+server. - sid is a service id, -1 to dump everything from the selected proxy. To do this I implemented a new session flag (SN_STAT_BOUND), added three variables in data_ctx.stats (iid, type, sid), modified dumpstats.c and completely revorked the process_uxst_stats: now it waits for a "\n" terminated string, splits args and uses them. BTW: It should be quite easy to add new commands, for example to enable/disable servers, the only problem I can see is a not very lucky config name (*stats* socket). :| During the work I also fixed two bug: - s->flags were not initialized for proto_uxst - missing comma if throttling not enabled (caused by a stupid change in "Implement persistent id for proxies and servers") Other changes: - No more magic type valuse, use STATS_TYPE_FE/STATS_TYPE_BE/STATS_TYPE_SV - Don't memset full s->data_ctx (it was clearing s->data_ctx.stats.{iid/type/sid}, instead initialize stats.sv & stats.sv_st (stats.px and stats.px_st were already initialized) With all that changes it was extremely easy to write a short perl plugin for a perl-enabled net-snmp (also included in this patch). 29385 is my PEN (Private Enterprise Number) and I'm willing to donate the SNMPv2-SMI::enterprises.29385.106.* OIDs for HAProxy if there is nothing assigned already.
2008-03-02 01:42:14 +00:00
s->flags = 0;
s->term_trace = 0;
[MAJOR] proto_uxst rework -> SNMP support Currently there is a ~16KB limit for a data size passed via unix socket. It is caused by a trivial bug ttat is going to fixed soon, however in most cases there is no need to dump a full stats. This patch makes possible to select a scope of dumped data by extending current "show stat" to "show stat [<iid> <type> <sid>]": - iid is a proxy id, -1 to dump all proxies - type selects type of dumpable objects: 1 for frontend, 2 for backend, 4 for server, -1 for all types. Values can be ORed, for example: 1+2=3 -> frontend+backend. 1+2+4=7 -> frontend+backend+server. - sid is a service id, -1 to dump everything from the selected proxy. To do this I implemented a new session flag (SN_STAT_BOUND), added three variables in data_ctx.stats (iid, type, sid), modified dumpstats.c and completely revorked the process_uxst_stats: now it waits for a "\n" terminated string, splits args and uses them. BTW: It should be quite easy to add new commands, for example to enable/disable servers, the only problem I can see is a not very lucky config name (*stats* socket). :| During the work I also fixed two bug: - s->flags were not initialized for proto_uxst - missing comma if throttling not enabled (caused by a stupid change in "Implement persistent id for proxies and servers") Other changes: - No more magic type valuse, use STATS_TYPE_FE/STATS_TYPE_BE/STATS_TYPE_SV - Don't memset full s->data_ctx (it was clearing s->data_ctx.stats.{iid/type/sid}, instead initialize stats.sv & stats.sv_st (stats.px and stats.px_st were already initialized) With all that changes it was extremely easy to write a short perl plugin for a perl-enabled net-snmp (also included in this patch). 29385 is my PEN (Private Enterprise Number) and I'm willing to donate the SNMPv2-SMI::enterprises.29385.106.* OIDs for HAProxy if there is nothing assigned already.
2008-03-02 01:42:14 +00:00
if ((t = task_new()) == NULL) {
Alert("out of memory in uxst_event_accept().\n");
goto out_free_session;
}
s->cli_addr = addr;
/* FIXME: should be checked earlier */
if (cfd >= global.maxsock) {
Alert("accept(): not enough free sockets. Raise -n argument. Giving up.\n");
goto out_free_task;
}
if (fcntl(cfd, F_SETFL, O_NONBLOCK) == -1) {
Alert("accept(): cannot set the socket in non blocking mode. Giving up\n");
goto out_free_task;
}
t->process = l->handler;
t->context = s;
t->nice = -64; /* we want to boost priority for local stats */
s->task = t;
s->listener = l;
s->fe = NULL;
s->be = NULL;
s->ana_state = 0;
s->req = s->rep = NULL; /* will be allocated later */
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].owner = t;
s->si[0].connect = NULL;
s->si[0].shutr = stream_sock_shutr;
s->si[0].shutw = stream_sock_shutw;
s->si[0].chk_rcv = stream_sock_chk_rcv;
s->si[0].chk_snd = stream_sock_chk_snd;
s->si[0].fd = cfd;
s->si[0].flags = SI_FL_NONE;
s->si[0].exp = TICK_ETERNITY;
s->si[1].state = s->si[1].prev_state = SI_ST_INI;
s->si[1].err_type = SI_ET_NONE;
s->si[1].err_loc = NULL;
s->si[1].owner = t;
s->si[1].connect = NULL;
s->si[1].shutr = stream_sock_shutr;
s->si[1].shutw = stream_sock_shutw;
s->si[1].chk_rcv = stream_sock_chk_rcv;
s->si[1].chk_snd = stream_sock_chk_snd;
s->si[1].exp = TICK_ETERNITY;
s->si[1].fd = -1; /* just to help with debugging */
s->si[1].flags = SI_FL_NONE;
s->srv = s->prev_srv = s->srv_conn = NULL;
s->pend_pos = NULL;
memset(&s->logs, 0, sizeof(s->logs));
memset(&s->txn, 0, sizeof(s->txn));
s->logs.tv_accept = now; /* corrected date for internal use */
s->data_state = DATA_ST_INIT;
s->data_source = DATA_SRC_NONE;
s->uniq_id = totalconn;
if ((s->req = pool_alloc2(pool2_buffer)) == NULL)
goto out_free_task;
buffer_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 |= BF_READ_ATTACHED; /* the producer is already connected */
s->req->flags |= BF_READ_DONTWAIT; /* we plan to read small requests */
s->req->analysers = l->analysers;
s->req->wto = TICK_ETERNITY;
s->req->cto = TICK_ETERNITY;
s->req->rto = TICK_ETERNITY;
if ((s->rep = pool_alloc2(pool2_buffer)) == NULL)
goto out_free_req;
buffer_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 = TICK_ETERNITY;
s->rep->cto = TICK_ETERNITY;
s->rep->wto = TICK_ETERNITY;
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;
if (l->timeout) {
s->req->rto = *l->timeout;
s->rep->wto = *l->timeout;
}
fd_insert(cfd);
fdtab[cfd].owner = &s->si[0];
fdtab[cfd].state = FD_STREADY;
fdtab[cfd].cb[DIR_RD].f = l->proto->read;
fdtab[cfd].cb[DIR_RD].b = s->req;
fdtab[cfd].cb[DIR_WR].f = l->proto->write;
fdtab[cfd].cb[DIR_WR].b = s->rep;
fdtab[cfd].peeraddr = (struct sockaddr *)&s->cli_addr;
fdtab[cfd].peerlen = sizeof(s->cli_addr);
EV_FD_SET(cfd, DIR_RD);
task_wakeup(t, TASK_WOKEN_INIT);
l->nbconn++; /* warning! right now, it's up to the handler to decrease this */
if (l->nbconn >= l->maxconn) {
EV_FD_CLR(l->fd, DIR_RD);
l->state = LI_FULL;
}
actconn++;
totalconn++;
}
return 0;
out_free_req:
pool_free2(pool2_buffer, s->req);
out_free_task:
task_free(t);
out_free_session:
LIST_DEL(&s->list);
pool_free2(pool2_session, s);
out_close:
close(cfd);
return 0;
}
/* Parses the request line in <cmd> and possibly starts dumping stats on
* s->rep with the hijack bit set. Returns 1 if OK, 0 in case of any error.
* The line is modified after parsing.
*/
int unix_sock_parse_request(struct session *s, char *line)
{
char *args[MAX_UXST_ARGS + 1];
int arg;
while (isspace((unsigned char)*line))
line++;
arg = 0;
args[arg] = line;
while (*line && arg < MAX_UXST_ARGS) {
if (isspace((unsigned char)*line)) {
*line++ = '\0';
while (isspace((unsigned char)*line))
line++;
args[++arg] = line;
continue;
}
line++;
}
while (++arg <= MAX_UXST_ARGS)
args[arg] = line;
if (strcmp(args[0], "show") == 0) {
if (strcmp(args[1], "stat") == 0) {
if (*args[2] && *args[3] && *args[4]) {
s->data_ctx.stats.flags |= STAT_BOUND;
s->data_ctx.stats.iid = atoi(args[2]);
s->data_ctx.stats.type = atoi(args[3]);
s->data_ctx.stats.sid = atoi(args[4]);
}
s->data_ctx.stats.flags |= STAT_SHOW_STAT;
s->data_ctx.stats.flags |= STAT_FMT_CSV;
s->ana_state = STATS_ST_REP;
buffer_install_hijacker(s, s->rep, stats_dump_raw_to_buffer);
}
else if (strcmp(args[1], "info") == 0) {
s->data_ctx.stats.flags |= STAT_SHOW_INFO;
s->data_ctx.stats.flags |= STAT_FMT_CSV;
s->ana_state = STATS_ST_REP;
buffer_install_hijacker(s, s->rep, stats_dump_raw_to_buffer);
}
else if (strcmp(args[1], "sess") == 0) {
s->ana_state = STATS_ST_REP;
buffer_install_hijacker(s, s->rep, stats_dump_sess_to_buffer);
}
else if (strcmp(args[1], "errors") == 0) {
if (*args[2])
s->data_ctx.errors.iid = atoi(args[2]);
else
s->data_ctx.errors.iid = -1;
s->data_ctx.errors.px = NULL;
s->ana_state = STATS_ST_REP;
buffer_install_hijacker(s, s->rep, stats_dump_errors_to_buffer);
}
else { /* neither "stat" nor "info" nor "sess" */
return 0;
}
}
else { /* not "show" */
return 0;
}
return 1;
}
/* Processes the stats interpreter on the statistics socket.
* In order to ease the transition, we simply simulate the server status
* for now. It only knows states STATS_ST_INIT, STATS_ST_REQ, STATS_ST_REP, and
* STATS_ST_CLOSE. It removes the AN_REQ_UNIX_STATS bit from req->analysers
* once done. It always returns 0.
*/
int uxst_req_analyser_stats(struct session *s, struct buffer *req, int an_bit)
{
char *line, *p;
switch (s->ana_state) {
case STATS_ST_INIT:
/* Stats output not initialized yet */
memset(&s->data_ctx.stats, 0, sizeof(s->data_ctx.stats));
s->data_source = DATA_SRC_STATS;
s->ana_state = STATS_ST_REQ;
buffer_write_dis(s->req);
buffer_shutw_now(s->req);
/* fall through */
case STATS_ST_REQ:
/* Now, stats are initialized, hijack is not set, and
* we are waiting for a complete request line.
*/
line = s->req->data;
p = memchr(line, '\n', s->req->l);
if (p) {
*p = '\0';
if (!unix_sock_parse_request(s, line)) {
/* invalid request */
stream_int_retnclose(s->req->prod, &unix_sock_usage);
s->ana_state = 0;
req->analysers = 0;
return 0;
}
}
/* processing a valid or incomplete request */
if ((req->flags & BF_FULL) || /* invalid request */
(req->flags & BF_READ_ERROR) || /* input error */
(req->flags & BF_READ_TIMEOUT) || /* read timeout */
tick_is_expired(req->analyse_exp, now_ms) || /* request timeout */
(req->flags & BF_SHUTR)) { /* input closed */
buffer_shutw_now(s->rep);
s->ana_state = 0;
req->analysers = 0;
return 0;
}
/* don't forward nor abort */
req->flags |= BF_READ_DONTWAIT; /* we plan to read small requests */
return 0;
case STATS_ST_REP:
/* do nothing while response is being processed */
return 0;
case STATS_ST_CLOSE:
/* end of dump */
s->req->analysers &= ~an_bit;
s->ana_state = 0;
break;
}
return 0;
}
/* This function is the unix-stream equivalent of the global process_session().
* It is currently limited to unix-stream processing on control sockets such as
* stats, and has no server-side. The two functions should be merged into one
* once client and server sides are better delimited. Note that the server-side
* still exists but remains in SI_ST_INI state forever, so that any call is a
* NOP.
*/
struct task *uxst_process_session(struct task *t)
{
struct session *s = t->context;
int resync;
unsigned int rqf_last, rpf_last;
/* 1a: Check for low level timeouts if needed. We just set a flag on
* stream interfaces when their timeouts have expired.
*/
if (unlikely(t->state & TASK_WOKEN_TIMER)) {
stream_int_check_timeouts(&s->si[0]);
buffer_check_timeouts(s->req);
buffer_check_timeouts(s->rep);
}
s->req->flags &= ~BF_READ_NOEXP;
/* copy req/rep flags so that we can detect shutdowns */
rqf_last = s->req->flags;
rpf_last = s->rep->flags;
/* 1b: check for low-level errors reported at the stream interface. */
if (unlikely(s->si[0].flags & SI_FL_ERR)) {
if (s->si[0].state == SI_ST_EST || s->si[0].state == SI_ST_DIS) {
s->si[0].shutr(&s->si[0]);
s->si[0].shutw(&s->si[0]);
stream_int_report_error(&s->si[0]);
}
}
/* check buffer timeouts, and close the corresponding stream interfaces
* for future reads or writes. Note: this will also concern upper layers
* but we do not touch any other flag. We must be careful and correctly
* detect state changes when calling them.
*/
if (unlikely(s->req->flags & (BF_READ_TIMEOUT|BF_WRITE_TIMEOUT))) {
if (s->req->flags & BF_READ_TIMEOUT)
s->req->prod->shutr(s->req->prod);
if (s->req->flags & BF_WRITE_TIMEOUT)
s->req->cons->shutw(s->req->cons);
}
if (unlikely(s->rep->flags & (BF_READ_TIMEOUT|BF_WRITE_TIMEOUT))) {
if (s->rep->flags & BF_READ_TIMEOUT)
s->rep->prod->shutr(s->rep->prod);
if (s->rep->flags & BF_WRITE_TIMEOUT)
s->rep->cons->shutw(s->rep->cons);
}
/* Check for connection closure */
resync_stream_interface:
/* nothing special to be done on client side */
if (unlikely(s->req->prod->state == SI_ST_DIS))
s->req->prod->state = SI_ST_CLO;
/*
* Note: of the transient states (REQ, CER, DIS), only REQ may remain
* at this point.
*/
resync_request:
/**** Process layer 7 below ****/
resync = 0;
/* Analyse request */
if ((s->req->flags & BF_MASK_ANALYSER) ||
(s->req->flags ^ rqf_last) & BF_MASK_STATIC) {
unsigned int flags = s->req->flags;
if (s->req->prod->state >= SI_ST_EST) {
/* it's up to the analysers to reset write_ena */
buffer_write_ena(s->req);
/* We will call all analysers for which a bit is set in
* s->req->analysers, following the bit order from LSB
* to MSB. The analysers must remove themselves from
* the list when not needed. This while() loop is in
* fact a cleaner if().
*/
while (s->req->analysers) {
if (s->req->analysers & AN_REQ_UNIX_STATS)
if (!uxst_req_analyser_stats(s, s->req, AN_REQ_UNIX_STATS))
break;
/* Just make sure that nobody set a wrong flag causing an endless loop */
s->req->analysers &= AN_REQ_UNIX_STATS;
/* we don't want to loop anyway */
break;
}
}
s->req->flags &= BF_CLEAR_READ & BF_CLEAR_WRITE & BF_CLEAR_TIMEOUT;
flags &= BF_CLEAR_READ & BF_CLEAR_WRITE & BF_CLEAR_TIMEOUT;
if ((s->req->flags ^ flags) & BF_MASK_STATIC)
resync = 1;
}
/* if noone is interested in analysing data, let's forward everything */
if (!s->req->analysers && !(s->req->flags & BF_HIJACK))
s->req->send_max = s->req->l;
/* If noone is interested in analysing data, it's time to forward
* everything. We will wake up from time to time when either send_max
* or to_forward are reached.
*/
if (!s->req->analysers &&
!(s->req->flags & (BF_HIJACK|BF_SHUTW)) &&
(s->req->prod->state >= SI_ST_EST)) {
/* This buffer is freewheeling, there's no analyser nor hijacker
* attached to it. If any data are left in, we'll permit them to
* move.
*/
buffer_flush(s->req);
/* If the producer is still connected, we'll schedule large blocks
* of data to be forwarded from the producer to the consumer (which
* might possibly not be connected yet).
*/
if (!(s->req->flags & BF_SHUTR) &&
s->req->to_forward < FORWARD_DEFAULT_SIZE)
[MEDIUM] i/o: rework ->to_forward and ->send_max The way the buffers and stream interfaces handled ->to_forward was really not handy for multiple reasons. Now we've moved its control to the receive-side of the buffer, which is also responsible for keeping send_max up to date. This makes more sense as it now becomes possible to send some pre-formatted data followed by forwarded data. The following explanation has also been added to buffer.h to clarify the situation. Right now, tests show that the I/O is behaving extremely well. Some work will have to be done to adapt existing splice code though. /* Note about the buffer structure The buffer contains two length indicators, one to_forward counter and one send_max limit. First, it must be understood that the buffer is in fact split in two parts : - the visible data (->data, for ->l bytes) - the invisible data, typically in kernel buffers forwarded directly from the source stream sock to the destination stream sock (->splice_len bytes). Those are used only during forward. In order not to mix data streams, the producer may only feed the invisible data with data to forward, and only when the visible buffer is empty. The consumer may not always be able to feed the invisible buffer due to platform limitations (lack of kernel support). Conversely, the consumer must always take data from the invisible data first before ever considering visible data. There is no limit to the size of data to consume from the invisible buffer, as platform-specific implementations will rarely leave enough control on this. So any byte fed into the invisible buffer is expected to reach the destination file descriptor, by any means. However, it's the consumer's responsibility to ensure that the invisible data has been entirely consumed before consuming visible data. This must be reflected by ->splice_len. This is very important as this and only this can ensure strict ordering of data between buffers. The producer is responsible for decreasing ->to_forward and increasing ->send_max. The ->to_forward parameter indicates how many bytes may be fed into either data buffer without waking the parent up. The ->send_max parameter says how many bytes may be read from the visible buffer. Thus it may never exceed ->l. This parameter is updated by any buffer_write() as well as any data forwarded through the visible buffer. The consumer is responsible for decreasing ->send_max when it sends data from the visible buffer, and ->splice_len when it sends data from the invisible buffer. A real-world example consists in part in an HTTP response waiting in a buffer to be forwarded. We know the header length (300) and the amount of data to forward (content-length=9000). The buffer already contains 1000 bytes of data after the 300 bytes of headers. Thus the caller will set ->send_max to 300 indicating that it explicitly wants to send those data, and set ->to_forward to 9000 (content-length). This value must be normalised immediately after updating ->to_forward : since there are already 1300 bytes in the buffer, 300 of which are already counted in ->send_max, and that size is smaller than ->to_forward, we must update ->send_max to 1300 to flush the whole buffer, and reduce ->to_forward to 8000. After that, the producer may try to feed the additional data through the invisible buffer using a platform-specific method such as splice(). */
2009-01-07 23:09:41 +00:00
buffer_forward(s->req, FORWARD_DEFAULT_SIZE);
}
/* reflect what the L7 analysers have seen last */
rqf_last = s->req->flags;
/*
* Now forward all shutdown requests between both sides of the buffer
*/
/* first, let's check if the request buffer needs to shutdown(write) */
if (unlikely((s->req->flags & (BF_SHUTW|BF_SHUTW_NOW|BF_EMPTY|BF_HIJACK|BF_WRITE_ENA|BF_SHUTR)) ==
(BF_EMPTY|BF_WRITE_ENA|BF_SHUTR)))
buffer_shutw_now(s->req);
/* shutdown(write) pending */
if (unlikely((s->req->flags & (BF_SHUTW|BF_SHUTW_NOW)) == BF_SHUTW_NOW))
s->req->cons->shutw(s->req->cons);
/* shutdown(write) done on server side, we must stop the client too */
if (unlikely((s->req->flags & (BF_SHUTW|BF_SHUTR|BF_SHUTR_NOW)) == BF_SHUTW &&
!s->req->analysers))
buffer_shutr_now(s->req);
/* shutdown(read) pending */
if (unlikely((s->req->flags & (BF_SHUTR|BF_SHUTR_NOW)) == BF_SHUTR_NOW))
s->req->prod->shutr(s->req->prod);
/*
* Here we want to check if we need to resync or not.
*/
if ((s->req->flags ^ rqf_last) & BF_MASK_STATIC)
resync = 1;
s->req->flags &= BF_CLEAR_READ & BF_CLEAR_WRITE & BF_CLEAR_TIMEOUT;
/* according to benchmarks, it makes sense to resync now */
if (s->req->prod->state == SI_ST_DIS)
goto resync_stream_interface;
if (resync)
goto resync_request;
resync_response:
resync = 0;
/* Analyse response */
if (unlikely(s->rep->flags & BF_HIJACK)) {
/* In inject mode, we wake up everytime something has
* happened on the write side of the buffer.
*/
unsigned int flags = s->rep->flags;
if ((s->rep->flags & (BF_WRITE_PARTIAL|BF_WRITE_ERROR|BF_SHUTW)) &&
!(s->rep->flags & BF_FULL)) {
s->rep->hijacker(s, s->rep);
}
s->rep->flags &= BF_CLEAR_READ & BF_CLEAR_WRITE & BF_CLEAR_TIMEOUT;
flags &= BF_CLEAR_READ & BF_CLEAR_WRITE & BF_CLEAR_TIMEOUT;
if ((s->rep->flags ^ flags) & BF_MASK_STATIC)
resync = 1;
}
else if ((s->rep->flags & BF_MASK_ANALYSER) ||
(s->rep->flags ^ rpf_last) & BF_MASK_STATIC) {
unsigned int flags = s->rep->flags;
if (s->rep->prod->state >= SI_ST_EST) {
/* it's up to the analysers to reset write_ena */
buffer_write_ena(s->rep);
}
s->rep->flags &= BF_CLEAR_READ & BF_CLEAR_WRITE & BF_CLEAR_TIMEOUT;
flags &= BF_CLEAR_READ & BF_CLEAR_WRITE & BF_CLEAR_TIMEOUT;
if ((s->rep->flags ^ flags) & BF_MASK_STATIC)
resync = 1;
}
/* If noone is interested in analysing data, it's time to forward
* everything. We will wake up from time to time when either send_max
* or to_forward are reached.
*/
if (!s->rep->analysers &&
!(s->rep->flags & (BF_HIJACK|BF_SHUTW)) &&
(s->rep->prod->state >= SI_ST_EST)) {
/* This buffer is freewheeling, there's no analyser nor hijacker
* attached to it. If any data are left in, we'll permit them to
* move.
*/
buffer_flush(s->rep);
/* If the producer is still connected, we'll schedule large blocks
* of data to be forwarded from the producer to the consumer (which
* might possibly not be connected yet).
*/
if (!(s->rep->flags & BF_SHUTR) &&
s->rep->to_forward < FORWARD_DEFAULT_SIZE)
[MEDIUM] i/o: rework ->to_forward and ->send_max The way the buffers and stream interfaces handled ->to_forward was really not handy for multiple reasons. Now we've moved its control to the receive-side of the buffer, which is also responsible for keeping send_max up to date. This makes more sense as it now becomes possible to send some pre-formatted data followed by forwarded data. The following explanation has also been added to buffer.h to clarify the situation. Right now, tests show that the I/O is behaving extremely well. Some work will have to be done to adapt existing splice code though. /* Note about the buffer structure The buffer contains two length indicators, one to_forward counter and one send_max limit. First, it must be understood that the buffer is in fact split in two parts : - the visible data (->data, for ->l bytes) - the invisible data, typically in kernel buffers forwarded directly from the source stream sock to the destination stream sock (->splice_len bytes). Those are used only during forward. In order not to mix data streams, the producer may only feed the invisible data with data to forward, and only when the visible buffer is empty. The consumer may not always be able to feed the invisible buffer due to platform limitations (lack of kernel support). Conversely, the consumer must always take data from the invisible data first before ever considering visible data. There is no limit to the size of data to consume from the invisible buffer, as platform-specific implementations will rarely leave enough control on this. So any byte fed into the invisible buffer is expected to reach the destination file descriptor, by any means. However, it's the consumer's responsibility to ensure that the invisible data has been entirely consumed before consuming visible data. This must be reflected by ->splice_len. This is very important as this and only this can ensure strict ordering of data between buffers. The producer is responsible for decreasing ->to_forward and increasing ->send_max. The ->to_forward parameter indicates how many bytes may be fed into either data buffer without waking the parent up. The ->send_max parameter says how many bytes may be read from the visible buffer. Thus it may never exceed ->l. This parameter is updated by any buffer_write() as well as any data forwarded through the visible buffer. The consumer is responsible for decreasing ->send_max when it sends data from the visible buffer, and ->splice_len when it sends data from the invisible buffer. A real-world example consists in part in an HTTP response waiting in a buffer to be forwarded. We know the header length (300) and the amount of data to forward (content-length=9000). The buffer already contains 1000 bytes of data after the 300 bytes of headers. Thus the caller will set ->send_max to 300 indicating that it explicitly wants to send those data, and set ->to_forward to 9000 (content-length). This value must be normalised immediately after updating ->to_forward : since there are already 1300 bytes in the buffer, 300 of which are already counted in ->send_max, and that size is smaller than ->to_forward, we must update ->send_max to 1300 to flush the whole buffer, and reduce ->to_forward to 8000. After that, the producer may try to feed the additional data through the invisible buffer using a platform-specific method such as splice(). */
2009-01-07 23:09:41 +00:00
buffer_forward(s->rep, FORWARD_DEFAULT_SIZE);
}
/* reflect what the L7 analysers have seen last */
rpf_last = s->rep->flags;
[MAJOR] proto_uxst rework -> SNMP support Currently there is a ~16KB limit for a data size passed via unix socket. It is caused by a trivial bug ttat is going to fixed soon, however in most cases there is no need to dump a full stats. This patch makes possible to select a scope of dumped data by extending current "show stat" to "show stat [<iid> <type> <sid>]": - iid is a proxy id, -1 to dump all proxies - type selects type of dumpable objects: 1 for frontend, 2 for backend, 4 for server, -1 for all types. Values can be ORed, for example: 1+2=3 -> frontend+backend. 1+2+4=7 -> frontend+backend+server. - sid is a service id, -1 to dump everything from the selected proxy. To do this I implemented a new session flag (SN_STAT_BOUND), added three variables in data_ctx.stats (iid, type, sid), modified dumpstats.c and completely revorked the process_uxst_stats: now it waits for a "\n" terminated string, splits args and uses them. BTW: It should be quite easy to add new commands, for example to enable/disable servers, the only problem I can see is a not very lucky config name (*stats* socket). :| During the work I also fixed two bug: - s->flags were not initialized for proto_uxst - missing comma if throttling not enabled (caused by a stupid change in "Implement persistent id for proxies and servers") Other changes: - No more magic type valuse, use STATS_TYPE_FE/STATS_TYPE_BE/STATS_TYPE_SV - Don't memset full s->data_ctx (it was clearing s->data_ctx.stats.{iid/type/sid}, instead initialize stats.sv & stats.sv_st (stats.px and stats.px_st were already initialized) With all that changes it was extremely easy to write a short perl plugin for a perl-enabled net-snmp (also included in this patch). 29385 is my PEN (Private Enterprise Number) and I'm willing to donate the SNMPv2-SMI::enterprises.29385.106.* OIDs for HAProxy if there is nothing assigned already.
2008-03-02 01:42:14 +00:00
/*
* Now forward all shutdown requests between both sides of the buffer
*/
[MAJOR] proto_uxst rework -> SNMP support Currently there is a ~16KB limit for a data size passed via unix socket. It is caused by a trivial bug ttat is going to fixed soon, however in most cases there is no need to dump a full stats. This patch makes possible to select a scope of dumped data by extending current "show stat" to "show stat [<iid> <type> <sid>]": - iid is a proxy id, -1 to dump all proxies - type selects type of dumpable objects: 1 for frontend, 2 for backend, 4 for server, -1 for all types. Values can be ORed, for example: 1+2=3 -> frontend+backend. 1+2+4=7 -> frontend+backend+server. - sid is a service id, -1 to dump everything from the selected proxy. To do this I implemented a new session flag (SN_STAT_BOUND), added three variables in data_ctx.stats (iid, type, sid), modified dumpstats.c and completely revorked the process_uxst_stats: now it waits for a "\n" terminated string, splits args and uses them. BTW: It should be quite easy to add new commands, for example to enable/disable servers, the only problem I can see is a not very lucky config name (*stats* socket). :| During the work I also fixed two bug: - s->flags were not initialized for proto_uxst - missing comma if throttling not enabled (caused by a stupid change in "Implement persistent id for proxies and servers") Other changes: - No more magic type valuse, use STATS_TYPE_FE/STATS_TYPE_BE/STATS_TYPE_SV - Don't memset full s->data_ctx (it was clearing s->data_ctx.stats.{iid/type/sid}, instead initialize stats.sv & stats.sv_st (stats.px and stats.px_st were already initialized) With all that changes it was extremely easy to write a short perl plugin for a perl-enabled net-snmp (also included in this patch). 29385 is my PEN (Private Enterprise Number) and I'm willing to donate the SNMPv2-SMI::enterprises.29385.106.* OIDs for HAProxy if there is nothing assigned already.
2008-03-02 01:42:14 +00:00
/*
* FIXME: this is probably where we should produce error responses.
*/
[MAJOR] proto_uxst rework -> SNMP support Currently there is a ~16KB limit for a data size passed via unix socket. It is caused by a trivial bug ttat is going to fixed soon, however in most cases there is no need to dump a full stats. This patch makes possible to select a scope of dumped data by extending current "show stat" to "show stat [<iid> <type> <sid>]": - iid is a proxy id, -1 to dump all proxies - type selects type of dumpable objects: 1 for frontend, 2 for backend, 4 for server, -1 for all types. Values can be ORed, for example: 1+2=3 -> frontend+backend. 1+2+4=7 -> frontend+backend+server. - sid is a service id, -1 to dump everything from the selected proxy. To do this I implemented a new session flag (SN_STAT_BOUND), added three variables in data_ctx.stats (iid, type, sid), modified dumpstats.c and completely revorked the process_uxst_stats: now it waits for a "\n" terminated string, splits args and uses them. BTW: It should be quite easy to add new commands, for example to enable/disable servers, the only problem I can see is a not very lucky config name (*stats* socket). :| During the work I also fixed two bug: - s->flags were not initialized for proto_uxst - missing comma if throttling not enabled (caused by a stupid change in "Implement persistent id for proxies and servers") Other changes: - No more magic type valuse, use STATS_TYPE_FE/STATS_TYPE_BE/STATS_TYPE_SV - Don't memset full s->data_ctx (it was clearing s->data_ctx.stats.{iid/type/sid}, instead initialize stats.sv & stats.sv_st (stats.px and stats.px_st were already initialized) With all that changes it was extremely easy to write a short perl plugin for a perl-enabled net-snmp (also included in this patch). 29385 is my PEN (Private Enterprise Number) and I'm willing to donate the SNMPv2-SMI::enterprises.29385.106.* OIDs for HAProxy if there is nothing assigned already.
2008-03-02 01:42:14 +00:00
/* first, let's check if the request buffer needs to shutdown(write) */
if (unlikely((s->rep->flags & (BF_SHUTW|BF_SHUTW_NOW|BF_EMPTY|BF_HIJACK|BF_WRITE_ENA|BF_SHUTR)) ==
(BF_EMPTY|BF_WRITE_ENA|BF_SHUTR)))
buffer_shutw_now(s->rep);
[MAJOR] proto_uxst rework -> SNMP support Currently there is a ~16KB limit for a data size passed via unix socket. It is caused by a trivial bug ttat is going to fixed soon, however in most cases there is no need to dump a full stats. This patch makes possible to select a scope of dumped data by extending current "show stat" to "show stat [<iid> <type> <sid>]": - iid is a proxy id, -1 to dump all proxies - type selects type of dumpable objects: 1 for frontend, 2 for backend, 4 for server, -1 for all types. Values can be ORed, for example: 1+2=3 -> frontend+backend. 1+2+4=7 -> frontend+backend+server. - sid is a service id, -1 to dump everything from the selected proxy. To do this I implemented a new session flag (SN_STAT_BOUND), added three variables in data_ctx.stats (iid, type, sid), modified dumpstats.c and completely revorked the process_uxst_stats: now it waits for a "\n" terminated string, splits args and uses them. BTW: It should be quite easy to add new commands, for example to enable/disable servers, the only problem I can see is a not very lucky config name (*stats* socket). :| During the work I also fixed two bug: - s->flags were not initialized for proto_uxst - missing comma if throttling not enabled (caused by a stupid change in "Implement persistent id for proxies and servers") Other changes: - No more magic type valuse, use STATS_TYPE_FE/STATS_TYPE_BE/STATS_TYPE_SV - Don't memset full s->data_ctx (it was clearing s->data_ctx.stats.{iid/type/sid}, instead initialize stats.sv & stats.sv_st (stats.px and stats.px_st were already initialized) With all that changes it was extremely easy to write a short perl plugin for a perl-enabled net-snmp (also included in this patch). 29385 is my PEN (Private Enterprise Number) and I'm willing to donate the SNMPv2-SMI::enterprises.29385.106.* OIDs for HAProxy if there is nothing assigned already.
2008-03-02 01:42:14 +00:00
/* shutdown(write) pending */
if (unlikely((s->rep->flags & (BF_SHUTW|BF_SHUTW_NOW)) == BF_SHUTW_NOW))
s->rep->cons->shutw(s->rep->cons);
[MAJOR] proto_uxst rework -> SNMP support Currently there is a ~16KB limit for a data size passed via unix socket. It is caused by a trivial bug ttat is going to fixed soon, however in most cases there is no need to dump a full stats. This patch makes possible to select a scope of dumped data by extending current "show stat" to "show stat [<iid> <type> <sid>]": - iid is a proxy id, -1 to dump all proxies - type selects type of dumpable objects: 1 for frontend, 2 for backend, 4 for server, -1 for all types. Values can be ORed, for example: 1+2=3 -> frontend+backend. 1+2+4=7 -> frontend+backend+server. - sid is a service id, -1 to dump everything from the selected proxy. To do this I implemented a new session flag (SN_STAT_BOUND), added three variables in data_ctx.stats (iid, type, sid), modified dumpstats.c and completely revorked the process_uxst_stats: now it waits for a "\n" terminated string, splits args and uses them. BTW: It should be quite easy to add new commands, for example to enable/disable servers, the only problem I can see is a not very lucky config name (*stats* socket). :| During the work I also fixed two bug: - s->flags were not initialized for proto_uxst - missing comma if throttling not enabled (caused by a stupid change in "Implement persistent id for proxies and servers") Other changes: - No more magic type valuse, use STATS_TYPE_FE/STATS_TYPE_BE/STATS_TYPE_SV - Don't memset full s->data_ctx (it was clearing s->data_ctx.stats.{iid/type/sid}, instead initialize stats.sv & stats.sv_st (stats.px and stats.px_st were already initialized) With all that changes it was extremely easy to write a short perl plugin for a perl-enabled net-snmp (also included in this patch). 29385 is my PEN (Private Enterprise Number) and I'm willing to donate the SNMPv2-SMI::enterprises.29385.106.* OIDs for HAProxy if there is nothing assigned already.
2008-03-02 01:42:14 +00:00
/* shutdown(write) done on the client side, we must stop the server too */
if (unlikely((s->rep->flags & (BF_SHUTW|BF_SHUTR|BF_SHUTR_NOW)) == BF_SHUTW))
buffer_shutr_now(s->rep);
[MAJOR] proto_uxst rework -> SNMP support Currently there is a ~16KB limit for a data size passed via unix socket. It is caused by a trivial bug ttat is going to fixed soon, however in most cases there is no need to dump a full stats. This patch makes possible to select a scope of dumped data by extending current "show stat" to "show stat [<iid> <type> <sid>]": - iid is a proxy id, -1 to dump all proxies - type selects type of dumpable objects: 1 for frontend, 2 for backend, 4 for server, -1 for all types. Values can be ORed, for example: 1+2=3 -> frontend+backend. 1+2+4=7 -> frontend+backend+server. - sid is a service id, -1 to dump everything from the selected proxy. To do this I implemented a new session flag (SN_STAT_BOUND), added three variables in data_ctx.stats (iid, type, sid), modified dumpstats.c and completely revorked the process_uxst_stats: now it waits for a "\n" terminated string, splits args and uses them. BTW: It should be quite easy to add new commands, for example to enable/disable servers, the only problem I can see is a not very lucky config name (*stats* socket). :| During the work I also fixed two bug: - s->flags were not initialized for proto_uxst - missing comma if throttling not enabled (caused by a stupid change in "Implement persistent id for proxies and servers") Other changes: - No more magic type valuse, use STATS_TYPE_FE/STATS_TYPE_BE/STATS_TYPE_SV - Don't memset full s->data_ctx (it was clearing s->data_ctx.stats.{iid/type/sid}, instead initialize stats.sv & stats.sv_st (stats.px and stats.px_st were already initialized) With all that changes it was extremely easy to write a short perl plugin for a perl-enabled net-snmp (also included in this patch). 29385 is my PEN (Private Enterprise Number) and I'm willing to donate the SNMPv2-SMI::enterprises.29385.106.* OIDs for HAProxy if there is nothing assigned already.
2008-03-02 01:42:14 +00:00
/* shutdown(read) pending */
if (unlikely((s->rep->flags & (BF_SHUTR|BF_SHUTR_NOW)) == BF_SHUTR_NOW))
s->rep->prod->shutr(s->rep->prod);
[MAJOR] proto_uxst rework -> SNMP support Currently there is a ~16KB limit for a data size passed via unix socket. It is caused by a trivial bug ttat is going to fixed soon, however in most cases there is no need to dump a full stats. This patch makes possible to select a scope of dumped data by extending current "show stat" to "show stat [<iid> <type> <sid>]": - iid is a proxy id, -1 to dump all proxies - type selects type of dumpable objects: 1 for frontend, 2 for backend, 4 for server, -1 for all types. Values can be ORed, for example: 1+2=3 -> frontend+backend. 1+2+4=7 -> frontend+backend+server. - sid is a service id, -1 to dump everything from the selected proxy. To do this I implemented a new session flag (SN_STAT_BOUND), added three variables in data_ctx.stats (iid, type, sid), modified dumpstats.c and completely revorked the process_uxst_stats: now it waits for a "\n" terminated string, splits args and uses them. BTW: It should be quite easy to add new commands, for example to enable/disable servers, the only problem I can see is a not very lucky config name (*stats* socket). :| During the work I also fixed two bug: - s->flags were not initialized for proto_uxst - missing comma if throttling not enabled (caused by a stupid change in "Implement persistent id for proxies and servers") Other changes: - No more magic type valuse, use STATS_TYPE_FE/STATS_TYPE_BE/STATS_TYPE_SV - Don't memset full s->data_ctx (it was clearing s->data_ctx.stats.{iid/type/sid}, instead initialize stats.sv & stats.sv_st (stats.px and stats.px_st were already initialized) With all that changes it was extremely easy to write a short perl plugin for a perl-enabled net-snmp (also included in this patch). 29385 is my PEN (Private Enterprise Number) and I'm willing to donate the SNMPv2-SMI::enterprises.29385.106.* OIDs for HAProxy if there is nothing assigned already.
2008-03-02 01:42:14 +00:00
/*
* Here we want to check if we need to resync or not.
*/
if ((s->rep->flags ^ rpf_last) & BF_MASK_STATIC)
resync = 1;
[MAJOR] proto_uxst rework -> SNMP support Currently there is a ~16KB limit for a data size passed via unix socket. It is caused by a trivial bug ttat is going to fixed soon, however in most cases there is no need to dump a full stats. This patch makes possible to select a scope of dumped data by extending current "show stat" to "show stat [<iid> <type> <sid>]": - iid is a proxy id, -1 to dump all proxies - type selects type of dumpable objects: 1 for frontend, 2 for backend, 4 for server, -1 for all types. Values can be ORed, for example: 1+2=3 -> frontend+backend. 1+2+4=7 -> frontend+backend+server. - sid is a service id, -1 to dump everything from the selected proxy. To do this I implemented a new session flag (SN_STAT_BOUND), added three variables in data_ctx.stats (iid, type, sid), modified dumpstats.c and completely revorked the process_uxst_stats: now it waits for a "\n" terminated string, splits args and uses them. BTW: It should be quite easy to add new commands, for example to enable/disable servers, the only problem I can see is a not very lucky config name (*stats* socket). :| During the work I also fixed two bug: - s->flags were not initialized for proto_uxst - missing comma if throttling not enabled (caused by a stupid change in "Implement persistent id for proxies and servers") Other changes: - No more magic type valuse, use STATS_TYPE_FE/STATS_TYPE_BE/STATS_TYPE_SV - Don't memset full s->data_ctx (it was clearing s->data_ctx.stats.{iid/type/sid}, instead initialize stats.sv & stats.sv_st (stats.px and stats.px_st were already initialized) With all that changes it was extremely easy to write a short perl plugin for a perl-enabled net-snmp (also included in this patch). 29385 is my PEN (Private Enterprise Number) and I'm willing to donate the SNMPv2-SMI::enterprises.29385.106.* OIDs for HAProxy if there is nothing assigned already.
2008-03-02 01:42:14 +00:00
s->rep->flags &= BF_CLEAR_READ & BF_CLEAR_WRITE & BF_CLEAR_TIMEOUT;
[MAJOR] proto_uxst rework -> SNMP support Currently there is a ~16KB limit for a data size passed via unix socket. It is caused by a trivial bug ttat is going to fixed soon, however in most cases there is no need to dump a full stats. This patch makes possible to select a scope of dumped data by extending current "show stat" to "show stat [<iid> <type> <sid>]": - iid is a proxy id, -1 to dump all proxies - type selects type of dumpable objects: 1 for frontend, 2 for backend, 4 for server, -1 for all types. Values can be ORed, for example: 1+2=3 -> frontend+backend. 1+2+4=7 -> frontend+backend+server. - sid is a service id, -1 to dump everything from the selected proxy. To do this I implemented a new session flag (SN_STAT_BOUND), added three variables in data_ctx.stats (iid, type, sid), modified dumpstats.c and completely revorked the process_uxst_stats: now it waits for a "\n" terminated string, splits args and uses them. BTW: It should be quite easy to add new commands, for example to enable/disable servers, the only problem I can see is a not very lucky config name (*stats* socket). :| During the work I also fixed two bug: - s->flags were not initialized for proto_uxst - missing comma if throttling not enabled (caused by a stupid change in "Implement persistent id for proxies and servers") Other changes: - No more magic type valuse, use STATS_TYPE_FE/STATS_TYPE_BE/STATS_TYPE_SV - Don't memset full s->data_ctx (it was clearing s->data_ctx.stats.{iid/type/sid}, instead initialize stats.sv & stats.sv_st (stats.px and stats.px_st were already initialized) With all that changes it was extremely easy to write a short perl plugin for a perl-enabled net-snmp (also included in this patch). 29385 is my PEN (Private Enterprise Number) and I'm willing to donate the SNMPv2-SMI::enterprises.29385.106.* OIDs for HAProxy if there is nothing assigned already.
2008-03-02 01:42:14 +00:00
if (s->req->prod->state == SI_ST_DIS)
goto resync_stream_interface;
[MAJOR] proto_uxst rework -> SNMP support Currently there is a ~16KB limit for a data size passed via unix socket. It is caused by a trivial bug ttat is going to fixed soon, however in most cases there is no need to dump a full stats. This patch makes possible to select a scope of dumped data by extending current "show stat" to "show stat [<iid> <type> <sid>]": - iid is a proxy id, -1 to dump all proxies - type selects type of dumpable objects: 1 for frontend, 2 for backend, 4 for server, -1 for all types. Values can be ORed, for example: 1+2=3 -> frontend+backend. 1+2+4=7 -> frontend+backend+server. - sid is a service id, -1 to dump everything from the selected proxy. To do this I implemented a new session flag (SN_STAT_BOUND), added three variables in data_ctx.stats (iid, type, sid), modified dumpstats.c and completely revorked the process_uxst_stats: now it waits for a "\n" terminated string, splits args and uses them. BTW: It should be quite easy to add new commands, for example to enable/disable servers, the only problem I can see is a not very lucky config name (*stats* socket). :| During the work I also fixed two bug: - s->flags were not initialized for proto_uxst - missing comma if throttling not enabled (caused by a stupid change in "Implement persistent id for proxies and servers") Other changes: - No more magic type valuse, use STATS_TYPE_FE/STATS_TYPE_BE/STATS_TYPE_SV - Don't memset full s->data_ctx (it was clearing s->data_ctx.stats.{iid/type/sid}, instead initialize stats.sv & stats.sv_st (stats.px and stats.px_st were already initialized) With all that changes it was extremely easy to write a short perl plugin for a perl-enabled net-snmp (also included in this patch). 29385 is my PEN (Private Enterprise Number) and I'm willing to donate the SNMPv2-SMI::enterprises.29385.106.* OIDs for HAProxy if there is nothing assigned already.
2008-03-02 01:42:14 +00:00
if (s->req->flags != rqf_last)
goto resync_request;
if (resync)
goto resync_response;
if (likely(s->rep->cons->state != SI_ST_CLO)) {
if (s->rep->cons->state == SI_ST_EST)
stream_sock_data_finish(s->rep->cons);
s->req->flags &= BF_CLEAR_READ & BF_CLEAR_WRITE & BF_CLEAR_TIMEOUT;
s->rep->flags &= BF_CLEAR_READ & BF_CLEAR_WRITE & BF_CLEAR_TIMEOUT;
s->si[0].prev_state = s->si[0].state;
s->si[0].flags &= ~(SI_FL_ERR|SI_FL_EXP);
/* Trick: if a request is being waiting for the server to respond,
* and if we know the server can timeout, we don't want the timeout
* to expire on the client side first, but we're still interested
* in passing data from the client to the server (eg: POST). Thus,
* we can cancel the client's request timeout if the server's
* request timeout is set and the server has not yet sent a response.
*/
if ((s->rep->flags & (BF_WRITE_ENA|BF_SHUTR)) == 0 &&
(tick_isset(s->req->wex) || tick_isset(s->rep->rex))) {
s->req->flags |= BF_READ_NOEXP;
s->req->rex = TICK_ETERNITY;
}
t->expire = tick_first(tick_first(s->req->rex, s->req->wex),
tick_first(s->rep->rex, s->rep->wex));
if (s->req->analysers)
t->expire = tick_first(t->expire, s->req->analyse_exp);
if (s->si[0].exp)
t->expire = tick_first(t->expire, s->si[0].exp);
return t;
}
actconn--;
if (s->listener) {
s->listener->nbconn--;
if (s->listener->state == LI_FULL &&
s->listener->nbconn < s->listener->maxconn) {
/* we should reactivate the listener */
EV_FD_SET(s->listener->fd, DIR_RD);
s->listener->state = LI_READY;
}
}
/* the task MUST not be in the run queue anymore */
session_free(s);
task_delete(t);
task_free(t);
return NULL;
}
__attribute__((constructor))
static void __uxst_protocol_init(void)
{
protocol_register(&proto_unix);
}
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/