PgStats older_stats;
/* database info to be sent to client */
- uint8_t welcome_msg[STARTUP_BUF]; /* ServerParams without VarCache ones */
- unsigned welcome_msg_len;
+ struct PktBuf *welcome_msg; /* ServerParams without VarCache ones */
VarCache orig_vars; /* default params from server */
bool db_auto; /* is the database auto-created by autodb_connstr */
bool admin; /* internal console db */
- uint8_t startup_params[STARTUP_BUF]; /* partial StartupMessage (without user) be sent to server */
- unsigned startup_params_len;
+ struct PktBuf *startup_params; /* partial StartupMessage (without user) be sent to server */
PgUser *forced_user; /* if not NULL, the user/psw is forced */
PktBuf *pktbuf_dynamic(int start_len) _MUSTCHECK;
void pktbuf_static(PktBuf *buf, uint8_t *data, int len);
+void pktbuf_free(PktBuf *buf);
+
+void pktbuf_reset(struct PktBuf *pkt);
+struct PktBuf *pktbuf_temp(void);
+
+
/*
* sending
*/
void log_server_error(const char *note, PktHdr *pkt);
void parse_server_error(PktHdr *pkt, const char **level_p, const char **msg_p);
-void add_welcome_parameter(PgPool *pool, const char *key, const char *val);
+bool add_welcome_parameter(PgPool *pool, const char *key, const char *val) _MUSTCHECK;
void finish_welcome_msg(PgSocket *server);
bool welcome_client(PgSocket *client) _MUSTCHECK;
{
struct msghdr msg;
struct cmsghdr *cmsg;
- int res;
struct iovec iovec;
- uint8_t pktbuf[STARTUP_BUF * 2];
+ int res;
uint8_t cntbuf[CMSG_SPACE(sizeof(int))];
- iovec.iov_base = pktbuf;
- BUILD_DataRow(res, pktbuf, sizeof(pktbuf), "issssiqissss",
+ struct PktBuf *pkt = pktbuf_temp();
+
+ pktbuf_write_DataRow(pkt, "issssiqissss",
fd, task, user, db, addr, port, ckey, link,
client_enc, std_strings, datestyle, timezone);
- if (res < 0)
+ if (pkt->failed)
return false;
- iovec.iov_len = res;
+ iovec.iov_base = pkt->buf;
+ iovec.iov_len = pktbuf_written(pkt);
/* sending fds */
memset(&msg, 0, sizeof(msg));
PgDatabase *db;
PgPool *pool;
PgUser *user;
- PktBuf msg;
+ PktBuf *msg;
int res;
/* fake database */
fatal("cannot create admin user?");
/* prepare welcome */
- pktbuf_static(&msg, pool->welcome_msg, sizeof(pool->welcome_msg));
- pktbuf_write_AuthenticationOk(&msg);
- pktbuf_write_ParameterStatus(&msg, "server_version", "8.0/bouncer");
- pktbuf_write_ParameterStatus(&msg, "client_encoding", "UNICODE");
- pktbuf_write_ParameterStatus(&msg, "server_encoding", "SQL_ASCII");
- pktbuf_write_ParameterStatus(&msg, "is_superuser", "on");
-
- pool->welcome_msg_len = pktbuf_written(&msg);
+ msg = pktbuf_dynamic(128);
+ if (!msg)
+ fatal("cannot create admin welcome");
+ pktbuf_write_AuthenticationOk(msg);
+ pktbuf_write_ParameterStatus(msg, "server_version", "8.0/bouncer");
+ pktbuf_write_ParameterStatus(msg, "client_encoding", "UNICODE");
+ pktbuf_write_ParameterStatus(msg, "server_encoding", "SQL_ASCII");
+ pktbuf_write_ParameterStatus(msg, "is_superuser", "on");
+
+ if (msg->failed)
+ fatal("admin welcome failed");
+
+ pool->welcome_msg = msg;
pool->welcome_msg_ready = 1;
- pktbuf_static(&msg, db->startup_params, sizeof(db->startup_params));
- pktbuf_put_string(&msg, "database");
- db->dbname = (char *)db->startup_params + pktbuf_written(&msg);
- pktbuf_put_string(&msg, "pgbouncer");
- db->startup_params_len = pktbuf_written(&msg);
+ msg = pktbuf_dynamic(128);
+ if (!msg)
+ fatal("cannot create admin startup pkt");
+ db->startup_params = msg;
+ pktbuf_put_string(msg, "database");
+ db->dbname = "pgbouncer";
+ pktbuf_put_string(msg, db->dbname);
/* initialize regexes */
res = regcomp(&rc_cmd, cmd_normal_rx, REG_EXTENDED | REG_ICASE);
close_server_list(&pool->tested_server_list, reason);
close_server_list(&pool->new_server_list, reason);
+ pktbuf_free(pool->welcome_msg);
+
list_del(&pool->map_head);
statlist_remove(&pool_list, &pool->head);
varcache_clean(&pool->orig_vars);
if (pool->db == db)
kill_pool(pool);
}
+ pktbuf_free(db->startup_params);
if (db->forced_user)
slab_free(user_cache, db->forced_user);
if (db->connect_query)
void parse_database(char *name, char *connstr)
{
char *p, *key, *val;
- PktBuf buf;
+ PktBuf *msg;
PgDatabase *db;
int pool_size = -1;
int res_pool_size = -1;
+ int dbname_ofs;
char *dbname = name;
char *host = NULL;
/* assign connect_query */
set_connect_query(db, connect_query);
- pktbuf_static(&buf, db->startup_params, sizeof(db->startup_params));
+ if (db->startup_params) {
+ msg = db->startup_params;
+ pktbuf_reset(msg);
+ } else {
+ msg = pktbuf_dynamic(128);
+ if (!msg)
+ fatal("cannot allocate startup buf");
+ db->startup_params = msg;
+ }
- pktbuf_put_string(&buf, "database");
- db->dbname = (char *)db->startup_params + pktbuf_written(&buf);
- pktbuf_put_string(&buf, dbname);
+ pktbuf_put_string(msg, "database");
+ dbname_ofs = msg->write_pos;
+ pktbuf_put_string(msg, dbname);
if (client_encoding) {
- pktbuf_put_string(&buf, "client_encoding");
- pktbuf_put_string(&buf, client_encoding);
+ pktbuf_put_string(msg, "client_encoding");
+ pktbuf_put_string(msg, client_encoding);
}
if (datestyle) {
- pktbuf_put_string(&buf, "datestyle");
- pktbuf_put_string(&buf, datestyle);
+ pktbuf_put_string(msg, "datestyle");
+ pktbuf_put_string(msg, datestyle);
}
if (timezone) {
- pktbuf_put_string(&buf, "timezone");
- pktbuf_put_string(&buf, timezone);
+ pktbuf_put_string(msg, "timezone");
+ pktbuf_put_string(msg, timezone);
}
- db->startup_params_len = pktbuf_written(&buf);
-
/* if user is forces, create fake object for it */
if (username != NULL) {
if (!force_user(db, username, password))
} else if (db->forced_user)
log_warning("losing forced user not supported,"
" keeping old setting");
+
+ /* remember dbname */
+ db->dbname = (char *)msg->buf + dbname_ofs;
}
/*
fatal("bad client state");
}
- if (!welcome_client(client)) {
+ /* check if we know server signature */
+ if (!client->pool->welcome_msg_ready) {
log_debug("finish_client_login: no welcome message, pause");
client->wait_for_welcome = 1;
pause_client(client);
}
client->wait_for_welcome = 0;
+ /* send the message */
+ if (!welcome_client(client))
+ return false;
+
slog_debug(client, "logged in");
return true;
#include "bouncer.h"
-static void pktbuf_free(PktBuf *buf)
+void pktbuf_free(PktBuf *buf)
{
- if (buf->fixed_buf)
+ if (!buf || buf->fixed_buf)
return;
log_debug("pktbuf_free(%p)", buf);
return buf;
}
+void pktbuf_reset(struct PktBuf *pkt)
+{
+ pkt->failed = 0;
+ pkt->write_pos = 0;
+ pkt->pktlen_pos = 0;
+ pkt->send_pos = 0;
+ pkt->sending = 0;
+}
+
void pktbuf_static(PktBuf *buf, uint8_t *data, int len)
{
memset(buf, 0, sizeof(*buf));
buf->fixed_buf = 1;
}
+struct PktBuf *pktbuf_temp(void)
+{
+ static PktBuf *temp_pktbuf;
+
+ if (!temp_pktbuf)
+ temp_pktbuf = pktbuf_dynamic(512);
+ if (!temp_pktbuf)
+ fatal("failed to create temp pktbuf");
+ pktbuf_reset(temp_pktbuf);
+ return temp_pktbuf;
+}
+
bool pktbuf_send_immidiate(PktBuf *buf, PgSocket *sk)
{
int fd = sbuf_socket(&sk->sbuf);
*/
/* add another server parameter packet to cache */
-void add_welcome_parameter(PgPool *pool, const char *key, const char *val)
+bool add_welcome_parameter(PgPool *pool, const char *key, const char *val)
{
- PktBuf msg;
+ PktBuf *msg = pool->welcome_msg;
if (pool->welcome_msg_ready)
- return;
+ return true;
- pktbuf_static(&msg, pool->welcome_msg + pool->welcome_msg_len,
- sizeof(pool->welcome_msg) - pool->welcome_msg_len);
+ if (!msg) {
+ msg = pktbuf_dynamic(128);
+ if (!msg)
+ return false;
+ pool->welcome_msg = msg;
+ }
/* first packet must be AuthOk */
- if (pool->welcome_msg_len == 0)
- pktbuf_write_AuthenticationOk(&msg);
+ if (msg->write_pos == 0)
+ pktbuf_write_AuthenticationOk(msg);
/* if not stored in ->orig_vars, write full packet */
- if (!varcache_set(&pool->orig_vars, key, val)) {
- pktbuf_write_ParameterStatus(&msg, key, val);
- pool->welcome_msg_len += pktbuf_written(&msg);
- }
+ if (!varcache_set(&pool->orig_vars, key, val))
+ pktbuf_write_ParameterStatus(msg, key, val);
+
+ return !msg->failed;
}
/* all parameters processed */
bool welcome_client(PgSocket *client)
{
int res;
- uint8_t buf[STARTUP_BUF];
- PktBuf msg;
PgPool *pool = client->pool;
+ const PktBuf *pmsg = pool->welcome_msg;
+ PktBuf *msg;
slog_noise(client, "P: welcome_client");
- if (!pool->welcome_msg_ready)
- return false;
- pktbuf_static(&msg, buf, sizeof(buf));
- pktbuf_put_bytes(&msg, pool->welcome_msg, pool->welcome_msg_len);
+ /* copy prepared stuff around */
+ msg = pktbuf_temp();
+ pktbuf_put_bytes(msg, pmsg->buf, pmsg->write_pos);
+ /* fill vars */
varcache_fill_unset(&pool->orig_vars, client);
- varcache_add_params(&msg, &client->vars);
+ varcache_add_params(msg, &client->vars);
/* give each client its own cancel key */
get_random_bytes(client->cancel_key, 8);
- pktbuf_write_BackendKeyData(&msg, client->cancel_key);
- pktbuf_write_ReadyForQuery(&msg);
+ pktbuf_write_BackendKeyData(msg, client->cancel_key);
- /* send all together */
- res = pktbuf_send_immidiate(&msg, client);
- if (!res)
- slog_warning(client, "unhandled failure to send welcome_msg");
+ /* finish */
+ pktbuf_write_ReadyForQuery(msg);
+ if (msg->failed) {
+ disconnect_client(client, true, "failed to prepare welcome message");
+ return false;
+ }
+ /* send all together */
+ res = pktbuf_send_immidiate(msg, client);
+ if (!res) {
+ disconnect_client(client, true, "failed to send welcome message");
+ return false;
+ }
return true;
}
{
PgDatabase *db = server->pool->db;
const char *username = server->pool->user->name;
- PktBuf pkt;
- uint8_t buf[STARTUP_BUF];
-
- pktbuf_static(&pkt, buf, sizeof(buf));
- pktbuf_write_StartupMessage(&pkt, username,
- db->startup_params,
- db->startup_params_len);
- return pktbuf_send_immidiate(&pkt, server);
+ PktBuf *pkt;
+
+ pkt = pktbuf_temp();
+ pktbuf_write_StartupMessage(pkt, username,
+ db->startup_params->buf,
+ db->startup_params->write_pos);
+ return pktbuf_send_immidiate(pkt, server);
}
int scan_text_result(struct MBuf *pkt, const char *tupdesc, ...)
varcache_set(&client->vars, key, val);
}
- if (startup)
- add_welcome_parameter(server->pool, key, val);
+ if (startup) {
+ if (!add_welcome_parameter(server->pool, key, val))
+ goto failed_store;
+ }
return true;
failed:
disconnect_server(server, true, "broken ParameterStatus packet");
return false;
+failed_store:
+ disconnect_server(server, true, "failed to store ParameterStatus");
+ return false;
}
/* we cannot log in at all, notify clients */
bool varcache_apply(PgSocket *server, PgSocket *client, bool *changes_p)
{
- PktBuf pkt;
- uint8_t buf[STARTUP_BUF];
int changes = 0;
struct PStr *cval, *sval;
const struct var_lookup *lk;
- uint8_t *debug_sql;
+ int sql_ofs;
bool std_quote = is_std_quote(&server->vars);
+ struct PktBuf *pkt = pktbuf_temp();
- pktbuf_static(&pkt, buf, sizeof(buf));
- pktbuf_start_packet(&pkt, 'Q');
+ pktbuf_start_packet(pkt, 'Q');
/* grab quory position inside pkt */
- debug_sql = pkt.buf + pkt.write_pos;
+ sql_ofs = pktbuf_written(pkt);
for (lk = lookup; lk->name; lk++) {
sval = get_value(&server->vars, lk);
cval = get_value(&client->vars, lk);
if (cval)
- changes += apply_var(&pkt, lk->name, cval->str, sval->str, std_quote);
+ changes += apply_var(pkt, lk->name, cval->str, sval->str, std_quote);
}
*changes_p = changes > 0;
if (!changes)
return true;
- pktbuf_put_char(&pkt, 0);
- pktbuf_finish_packet(&pkt);
+ pktbuf_put_char(pkt, 0);
+ pktbuf_finish_packet(pkt);
- slog_debug(server, "varcache_apply: %s", debug_sql);
- return pktbuf_send_immidiate(&pkt, server);
+ slog_debug(server, "varcache_apply: %s", pkt->buf + sql_ofs);
+ return pktbuf_send_immidiate(pkt, server);
}
void varcache_fill_unset(VarCache *src, PgSocket *dst)