pgbouncer-1.7/0000775000175000017500000000000012635051616010355 500000000000000pgbouncer-1.7/src/0000775000175000017500000000000012635051616011144 500000000000000pgbouncer-1.7/src/loader.c0000664000175000017500000003136312572127367012513 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Config and pg_auth file reading. */ #include "bouncer.h" #ifdef HAVE_NETDB_H #include #endif #include /* * ConnString parsing */ /* just skip whitespace */ static char *cstr_skip_ws(char *p) { while (*p && *p == ' ') p++; return p; } /* parse parameter name before '=' */ static char *cstr_get_key(char *p, char **dst_p) { char *end; p = cstr_skip_ws(p); *dst_p = p; while (*p && *p != '=' && *p != ' ') p++; end = p; p = cstr_skip_ws(p); /* fail if no '=' or empty name */ if (*p != '=' || *dst_p == end) return NULL; *end = 0; return p + 1; } /* unquote the quoted value after first quote */ static char *cstr_unquote_value(char *p) { char *s = p; while (1) { if (!*p) return NULL; if (p[0] == '\'') { if (p[1] == '\'') p++; else break; } *s++ = *p++; } /* terminate actual value */ *s = 0; /* return position after quote */ return p + 1; } /* parse value, possibly quoted */ static char *cstr_get_value(char *p, char **dst_p) { p = cstr_skip_ws(p); if (*p == '\'') { *dst_p = ++p; p = cstr_unquote_value(p); if (!p) return NULL; } else { *dst_p = p; while (*p && *p != ' ') p++; } if (*p) { /* if not EOL, cut value */ *p = 0; p++; } /* disallow empty values */ if (*dst_p[0] == 0) return NULL; return p; } /* * Get key=val pair from connstring. Returns position it stopped * or NULL on error. EOF is signaled by *key = 0. */ static char * cstr_get_pair(char *p, char **key_p, char **val_p) { p = cstr_skip_ws(p); *key_p = *val_p = p; if (*p == 0) return p; /* read key */ p = cstr_get_key(p, key_p); if (!p) return NULL; /* read value */ p = cstr_get_value(p, val_p); if (!p) return NULL; log_noise("cstr_get_pair: \"%s\"=\"%s\"", *key_p, *val_p); return cstr_skip_ws(p); } static void set_connect_query(PgDatabase *db, const char *new) { const char *old = db->connect_query; char *val = NULL; if (old && new) { if (strcmp(old, new) == 0) return; val = strdup(new); if (val) { free((void *)old); db->connect_query = val; } } else if (new) { val = strdup(new); db->connect_query = val; } else { free((void *)db->connect_query); db->connect_query = NULL; } if (new && !val) log_error("no memory, cannot assign connect_query for %s", db->name); } static void set_autodb(const char *connstr) { char *tmp = strdup(connstr); char *old = cf_autodb_connstr; if (!tmp) { log_warning("no mem to change autodb_connstr"); return; } cf_autodb_connstr = tmp; if (old) { if (strcmp(connstr, old) != 0) tag_autodb_dirty(); free(old); } } /* fill PgDatabase from connstr */ bool parse_database(void *base, const char *name, const char *connstr) { char *p, *key, *val; PktBuf *msg; PgDatabase *db; struct CfValue cv; int pool_size = -1; int res_pool_size = -1; int max_db_connections = -1; int dbname_ofs; int pool_mode = POOL_INHERIT; char *tmp_connstr; const char *dbname = name; char *host = NULL; char *port = "5432"; char *username = NULL; char *password = ""; char *auth_username = NULL; char *client_encoding = NULL; char *datestyle = NULL; char *timezone = NULL; char *connect_query = NULL; char *appname = NULL; int v_port; cv.value_p = &pool_mode; cv.extra = (const void *)pool_mode_map; if (strcmp(name, "*") == 0) { set_autodb(connstr); return true; } tmp_connstr = strdup(connstr); if (!tmp_connstr) return false; p = tmp_connstr; while (*p) { p = cstr_get_pair(p, &key, &val); if (p == NULL) { log_error("%s: syntax error in connstring", name); goto fail; } else if (!key[0]) { break; } if (strcmp("dbname", key) == 0) { dbname = val; } else if (strcmp("host", key) == 0) { host = val; } else if (strcmp("port", key) == 0) { port = val; } else if (strcmp("user", key) == 0) { username = val; } else if (strcmp("password", key) == 0) { password = val; } else if (strcmp("auth_user", key) == 0) { auth_username = val; } else if (strcmp("client_encoding", key) == 0) { client_encoding = val; } else if (strcmp("datestyle", key) == 0) { datestyle = val; } else if (strcmp("timezone", key) == 0) { timezone = val; } else if (strcmp("pool_size", key) == 0) { pool_size = atoi(val); } else if (strcmp("reserve_pool", key) == 0) { res_pool_size = atoi(val); } else if (strcmp("max_db_connections", key) == 0) { max_db_connections = atoi(val); } else if (strcmp("pool_mode", key) == 0) { if (!cf_set_lookup(&cv, val)) { log_error("skipping database %s because" " of invalid pool mode: %s", name, val); goto fail; } } else if (strcmp("connect_query", key) == 0) { connect_query = val; } else if (strcmp("application_name", key) == 0) { appname = val; } else { log_error("skipping database %s because" " of unknown parameter in connstring: %s", name, key); goto fail; } } /* port= */ v_port = atoi(port); if (v_port == 0) { log_error("skipping database %s because" " of bad port: %s", name, port); goto fail; } db = add_database(name); if (!db) { log_error("cannot create database, no memory?"); goto fail; } /* host= */ if (host) { host = strdup(host); if (!host) { log_error("failed to allocate host="); goto fail; } } /* tag the db as alive */ db->db_dead = 0; /* assuming not an autodb */ db->db_auto = 0; db->inactive_time = 0; /* if updating old db, check if anything changed */ if (db->dbname) { bool changed = false; if (strcmp(db->dbname, dbname) != 0) { changed = true; } else if (!!host != !!db->host) { changed = true; } else if (host && strcmp(host, db->host) != 0) { changed = true; } else if (v_port != db->port) { changed = true; } else if (username && !db->forced_user) { changed = true; } else if (username && strcmp(username, db->forced_user->name) != 0) { changed = true; } else if (!username && db->forced_user) { changed = true; } else if ((db->connect_query && !connect_query) || (!db->connect_query && connect_query) || (connect_query && strcmp(connect_query, db->connect_query) != 0)) { changed = true; } if (changed) tag_database_dirty(db); } /* if pool_size < 0 it will be set later */ db->pool_size = pool_size; db->res_pool_size = res_pool_size; db->pool_mode = pool_mode; db->max_db_connections = max_db_connections; if (db->host) free(db->host); db->host = host; db->port = v_port; /* assign connect_query */ set_connect_query(db, connect_query); 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(msg, "database"); dbname_ofs = msg->write_pos; pktbuf_put_string(msg, dbname); if (client_encoding) { pktbuf_put_string(msg, "client_encoding"); pktbuf_put_string(msg, client_encoding); } if (datestyle) { pktbuf_put_string(msg, "datestyle"); pktbuf_put_string(msg, datestyle); } if (timezone) { pktbuf_put_string(msg, "timezone"); pktbuf_put_string(msg, timezone); } if (appname) { pktbuf_put_string(msg, "application_name"); pktbuf_put_string(msg, appname); } if (auth_username != NULL) { db->auth_user = find_user(auth_username); if (!db->auth_user) { db->auth_user = add_user(auth_username, ""); } } else if (db->auth_user) { db->auth_user = NULL; } /* if user is forced, create fake object for it */ if (username != NULL) { if (!force_user(db, username, password)) log_warning("db setup failed, trying to continue"); } else if (db->forced_user) { log_warning("losing forced user not supported," " keeping old setting"); } /* remember dbname */ db->dbname = (char *)msg->buf + dbname_ofs; free(tmp_connstr); return true; fail: free(tmp_connstr); return true; } bool parse_user(void *base, const char *name, const char *connstr) { char *p, *key, *val, *tmp_connstr; PgUser *user; struct CfValue cv; int pool_mode = POOL_INHERIT; int max_user_connections = -1; cv.value_p = &pool_mode; cv.extra = (const void *)pool_mode_map; tmp_connstr = strdup(connstr); if (!tmp_connstr) return false; p = tmp_connstr; while (*p) { p = cstr_get_pair(p, &key, &val); if (p == NULL) { log_error("%s: syntax error in user settings", name); goto fail; } else if (!key[0]) { break; } if (strcmp("pool_mode", key) == 0) { if (!cf_set_lookup(&cv, val)) { log_error("skipping user %s because" " of invalid pool mode: %s", name, val); goto fail; } } else if (strcmp("max_user_connections", key) == 0) { max_user_connections = atoi(val); } else { log_error("skipping user %s because" " of unknown parameter in settings: %s", name, key); goto fail; } } user = find_user(name); if (!user) { user = add_user(name, ""); if (!user) { log_error("cannot create user, no memory?"); goto fail; } } user->pool_mode = pool_mode; user->max_user_connections = max_user_connections; fail: free(tmp_connstr); return true; } /* * User file parsing */ /* find next " in string, skipping escaped ones */ static char *find_quote(char *p, bool start) { loop: while (*p && *p != '"') p++; if (p[0] == '"' && p[1] == '"' && !start) { p += 2; goto loop; } return p; } /* string is unquoted while copying */ static void copy_quoted(char *dst, const char *src, int len) { char *end = dst + len - 1; while (*src && dst < end) { if (*src == '"') src++; *dst++ = *src++; } *dst = 0; } static void unquote_add_user(const char *username, const char *password) { char real_user[MAX_USERNAME]; char real_passwd[MAX_PASSWORD]; PgUser *user; copy_quoted(real_user, username, sizeof(real_user)); copy_quoted(real_passwd, password, sizeof(real_passwd)); user = add_user(real_user, real_passwd); if (!user) log_warning("cannot create user, no memory"); } static bool auth_loaded(const char *fn) { static struct stat cache; struct stat cur; /* hack for resetting */ if (fn == NULL) { memset(&cache, 0, sizeof(cache)); return false; } if (stat(fn, &cur) < 0) return false; if (cache.st_dev == cur.st_dev && cache.st_ino == cur.st_ino && cache.st_mode == cur.st_mode && cache.st_uid == cur.st_uid && cache.st_gid == cur.st_gid && cache.st_mtime == cur.st_mtime && cache.st_size == cur.st_size) return true; cache = cur; return false; } bool loader_users_check(void) { if (auth_loaded(cf_auth_file)) return true; return load_auth_file(cf_auth_file); } static void disable_users(void) { PgUser *user; struct List *item; statlist_for_each(item, &user_list) { user = container_of(item, PgUser, head); user->passwd[0] = 0; } } /* load list of users from pg_auth/pg_psw file */ bool load_auth_file(const char *fn) { char *user, *password, *buf, *p; buf = load_file(fn, NULL); if (buf == NULL) { /* reset file info */ auth_loaded(NULL); return false; } log_debug("loading auth_file: \"%s\"", fn); disable_users(); p = buf; while (*p) { /* skip whitespace and empty lines */ while (*p && isspace(*p)) p++; if (!*p) break; /* skip commented-out lines */ if (*p == ';') { while (*p && *p != '\n') p++; continue; } /* start of line */ if (*p != '"') { log_error("broken auth file"); break; } user = ++p; p = find_quote(p, false); if (*p != '"') { log_error("broken auth file"); break; } if (p - user >= MAX_USERNAME) { log_error("username too long"); break; } *p++ = 0; /* tag username end */ /* get password */ p = find_quote(p, true); if (*p != '"') { log_error("broken auth file"); break; } password = ++p; p = find_quote(p, false); if (*p != '"') { log_error("broken auth file"); break; } if (p - password >= MAX_PASSWORD) { log_error("too long password"); break; } *p++ = 0; /* tag password end */ /* send them away */ unquote_add_user(user, password); /* skip rest of the line */ while (*p && *p != '\n') p++; } free(buf); return true; } pgbouncer-1.7/src/takeover.c0000664000175000017500000002153712565403475013066 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Connect to running bouncer process, load fds from it, shut it down * and continue with them. * * Each row from SHOW FDS will have corresponding fd in ancillary message. * * Manpages: unix, sendmsg, recvmsg, cmsg, readv */ #include "bouncer.h" /* * Takeover done, old process shut down, * kick this one running. */ static PgSocket *old_bouncer = NULL; void takeover_finish(void) { uint8_t buf[512]; int fd = sbuf_socket(&old_bouncer->sbuf); bool res; int got; log_info("sending SHUTDOWN;"); socket_set_nonblocking(fd, 0); SEND_generic(res, old_bouncer, 'Q', "s", "SHUTDOWN;"); if (!res) fatal("failed to send SHUTDOWN;"); while (1) { got = safe_recv(fd, buf, sizeof(buf), 0); if (got == 0) break; if (got < 0) fatal_perror("sky is falling - error while waiting result from SHUTDOWN"); } disconnect_server(old_bouncer, false, "disko over"); old_bouncer = NULL; log_info("old process killed, resuming work"); resume_all(); } static void takeover_finish_part1(PgSocket *bouncer) { Assert(old_bouncer == NULL); /* unregister bouncer from libevent */ if (!sbuf_pause(&bouncer->sbuf)) fatal_perror("sbuf_pause failed"); old_bouncer = bouncer; cf_reboot = 0; log_info("disko over, going background"); } /* parse msg for fd and info */ static void takeover_load_fd(struct MBuf *pkt, const struct cmsghdr *cmsg) { int fd; char *task, *saddr, *user, *db; char *client_enc, *std_string, *datestyle, *timezone, *password; int oldfd, port, linkfd; int got; uint64_t ckey; PgAddr addr; bool res = false; memset(&addr, 0, sizeof(addr)); if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS && cmsg->cmsg_len >= CMSG_LEN(sizeof(int))) { /* get the fd */ memcpy(&fd, CMSG_DATA(cmsg), sizeof(int)); log_debug("got fd: %d", fd); } else { fatal("broken fd packet"); } /* parse row contents */ got = scan_text_result(pkt, "issssiqisssss", &oldfd, &task, &user, &db, &saddr, &port, &ckey, &linkfd, &client_enc, &std_string, &datestyle, &timezone, &password); if (got < 0 || task == NULL || saddr == NULL) fatal("NULL data from old process"); log_debug("FD row: fd=%d(%d) linkfd=%d task=%s user=%s db=%s enc=%s", oldfd, fd, linkfd, task, user ? user : "NULL", db ? db : "NULL", client_enc ? client_enc : "NULL"); if (!password) password = ""; /* fill address */ if (strcmp(saddr, "unix") == 0) { pga_set(&addr, AF_UNIX, cf_listen_port); } else { if (!pga_pton(&addr, saddr, port)) fatal("failed to convert address: %s", saddr); } /* decide what to do with it */ if (strcmp(task, "client") == 0) { res = use_client_socket(fd, &addr, db, user, ckey, oldfd, linkfd, client_enc, std_string, datestyle, timezone, password); } else if (strcmp(task, "server") == 0) { res = use_server_socket(fd, &addr, db, user, ckey, oldfd, linkfd, client_enc, std_string, datestyle, timezone, password); } else if (strcmp(task, "pooler") == 0) { res = use_pooler_socket(fd, pga_is_unix(&addr)); } else { fatal("unknown task: %s", task); } if (!res) fatal("socket takeover failed - no mem?"); } static void takeover_create_link(PgPool *pool, PgSocket *client) { struct List *item; PgSocket *server; statlist_for_each(item, &pool->active_server_list) { server = container_of(item, PgSocket, head); if (server->tmp_sk_oldfd == client->tmp_sk_linkfd) { server->link = client; client->link = server; return; } } fatal("takeover_create_link: failed to find pair"); } /* clean the inappropriate places the old fds got stored in */ static void takeover_clean_socket_list(struct StatList *list) { struct List *item; PgSocket *sk; statlist_for_each(item, list) { sk = container_of(item, PgSocket, head); if (sk->suspended) { sk->tmp_sk_oldfd = get_cached_time(); sk->tmp_sk_linkfd = get_cached_time(); } } } /* all fds loaded, create links */ static void takeover_postprocess_fds(void) { struct List *item, *item2; PgSocket *client; PgPool *pool; statlist_for_each(item, &pool_list) { pool = container_of(item, PgPool, head); if (pool->db->admin) continue; statlist_for_each(item2, &pool->active_client_list) { client = container_of(item2, PgSocket, head); if (client->suspended && client->tmp_sk_linkfd) takeover_create_link(pool, client); } } statlist_for_each(item, &pool_list) { pool = container_of(item, PgPool, head); takeover_clean_socket_list(&pool->active_client_list); takeover_clean_socket_list(&pool->active_server_list); takeover_clean_socket_list(&pool->idle_server_list); } } static void next_command(PgSocket *bouncer, struct MBuf *pkt) { bool res = true; const char *cmd; if (!mbuf_get_string(pkt, &cmd)) fatal("bad result pkt"); log_debug("takeover_recv_fds: 'C' body: %s", cmd); if (strcmp(cmd, "SUSPEND") == 0) { log_info("SUSPEND finished, sending SHOW FDS"); SEND_generic(res, bouncer, 'Q', "s", "SHOW FDS;"); } else if (strncmp(cmd, "SHOW", 4) == 0) { /* all fds loaded, review them */ takeover_postprocess_fds(); log_info("SHOW FDS finished"); takeover_finish_part1(bouncer); } else { fatal("got bad CMD from old bouncer: %s", cmd); } if (!res) fatal("command send failed"); } static void takeover_parse_data(PgSocket *bouncer, struct msghdr *msg, struct MBuf *data) { struct cmsghdr *cmsg; PktHdr pkt; cmsg = msg->msg_controllen ? CMSG_FIRSTHDR(msg) : NULL; while (mbuf_avail_for_read(data) > 0) { if (!get_header(data, &pkt)) fatal("cannot parse packet"); /* * There should not be partial reads from UNIX socket. */ if (incomplete_pkt(&pkt)) fatal("unexpected partial packet"); switch (pkt.type) { case 'T': /* RowDescription */ log_debug("takeover_parse_data: 'T'"); break; case 'D': /* DataRow */ log_debug("takeover_parse_data: 'D'"); if (cmsg) { takeover_load_fd(&pkt.data, cmsg); cmsg = CMSG_NXTHDR(msg, cmsg); } else fatal("got row without fd info"); break; case 'Z': /* ReadyForQuery */ log_debug("takeover_parse_data: 'Z'"); break; case 'C': /* CommandComplete */ log_debug("takeover_parse_data: 'C'"); next_command(bouncer, &pkt.data); break; case 'E': /* ErrorMessage */ log_server_error("old bouncer sent", &pkt); fatal("something failed"); default: fatal("takeover_parse_data: unexpected pkt: '%c'", pkt_desc(&pkt)); } } } /* * listen for data from old bouncer. * * use always recvmsg, to keep code simpler */ static void takeover_recv_cb(int sock, short flags, void *arg) { PgSocket *bouncer = container_of(arg, PgSocket, sbuf); uint8_t data_buf[STARTUP_BUF * 2]; uint8_t cnt_buf[128]; struct msghdr msg; struct iovec io; int res; struct MBuf data; memset(&msg, 0, sizeof(msg)); io.iov_base = data_buf; io.iov_len = sizeof(data_buf); msg.msg_iov = &io; msg.msg_iovlen = 1; msg.msg_control = cnt_buf; msg.msg_controllen = sizeof(cnt_buf); res = safe_recvmsg(sock, &msg, 0); if (res > 0) { mbuf_init_fixed_reader(&data, data_buf, res); takeover_parse_data(bouncer, &msg, &data); } else if (res == 0) { fatal("unexpected EOF"); } else { if (errno == EAGAIN) return; fatal_perror("safe_recvmsg"); } } /* * login finished, send first command, * replace recv callback with custom recvmsg() based one. */ bool takeover_login(PgSocket *bouncer) { bool res; slog_info(bouncer, "Login OK, sending SUSPEND"); SEND_generic(res, bouncer, 'Q', "s", "SUSPEND;"); if (res) { /* use own callback */ if (!sbuf_pause(&bouncer->sbuf)) fatal("sbuf_pause failed"); res = sbuf_continue_with_callback(&bouncer->sbuf, takeover_recv_cb); if (!res) fatal("takeover_login: sbuf_continue_with_callback failed"); } else { fatal("takeover_login: failed to send command"); } return res; } /* launch connection to running process */ void takeover_init(void) { PgDatabase *db = find_database("pgbouncer"); PgPool *pool = get_pool(db, db->forced_user); if (!pool) fatal("no admin pool?"); log_info("takeover_init: launching connection"); launch_new_connection(pool); } void takeover_login_failed(void) { fatal("login failed"); } pgbouncer-1.7/src/sbuf.c0000664000175000017500000006630012630305277012174 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Stream buffer * * The task is to copy data from one socket to another * efficiently, while allowing callbacks to look * at packet headers. */ #include "bouncer.h" #ifdef USUAL_LIBSSL_FOR_TLS #define USE_TLS #endif /* sbuf_main_loop() skip_recv values */ #define DO_RECV false #define SKIP_RECV true #define ACT_UNSET 0 #define ACT_SEND 1 #define ACT_SKIP 2 #define ACT_CALL 3 enum TLSState { SBUF_TLS_NONE, SBUF_TLS_DO_HANDSHAKE, SBUF_TLS_IN_HANDSHAKE, SBUF_TLS_OK, }; enum WaitType { W_NONE = 0, W_CONNECT, W_RECV, W_SEND, W_ONCE }; #define AssertSanity(sbuf) do { \ Assert(iobuf_sane((sbuf)->io)); \ } while (0) #define AssertActive(sbuf) do { \ Assert((sbuf)->sock > 0); \ AssertSanity(sbuf); \ } while (0) /* declare static stuff */ static bool sbuf_queue_send(SBuf *sbuf) _MUSTCHECK; static bool sbuf_send_pending(SBuf *sbuf) _MUSTCHECK; static bool sbuf_process_pending(SBuf *sbuf) _MUSTCHECK; static void sbuf_connect_cb(int sock, short flags, void *arg); static void sbuf_recv_cb(int sock, short flags, void *arg); static void sbuf_send_cb(int sock, short flags, void *arg); static void sbuf_try_resync(SBuf *sbuf, bool release); static bool sbuf_wait_for_data(SBuf *sbuf) _MUSTCHECK; static void sbuf_main_loop(SBuf *sbuf, bool skip_recv); static bool sbuf_call_proto(SBuf *sbuf, int event) /* _MUSTCHECK */; static bool sbuf_actual_recv(SBuf *sbuf, unsigned len) _MUSTCHECK; static bool sbuf_after_connect_check(SBuf *sbuf) _MUSTCHECK; static bool handle_tls_handshake(SBuf *sbuf) /* _MUSTCHECK */; static inline IOBuf *get_iobuf(SBuf *sbuf) { return sbuf->io; } /* regular I/O */ static int raw_sbufio_recv(struct SBuf *sbuf, void *dst, unsigned int len); static int raw_sbufio_send(struct SBuf *sbuf, const void *data, unsigned int len); static int raw_sbufio_close(struct SBuf *sbuf); static const SBufIO raw_sbufio_ops = { raw_sbufio_recv, raw_sbufio_send, raw_sbufio_close }; /* I/O over TLS */ #ifdef USE_TLS static int tls_sbufio_recv(struct SBuf *sbuf, void *dst, unsigned int len); static int tls_sbufio_send(struct SBuf *sbuf, const void *data, unsigned int len); static int tls_sbufio_close(struct SBuf *sbuf); static const SBufIO tls_sbufio_ops = { tls_sbufio_recv, tls_sbufio_send, tls_sbufio_close }; static void sbuf_tls_handshake_cb(int fd, short flags, void *_sbuf); #endif /********************************* * Public functions *********************************/ /* initialize SBuf with proto handler */ void sbuf_init(SBuf *sbuf, sbuf_cb_t proto_fn) { memset(sbuf, 0, sizeof(SBuf)); sbuf->proto_cb = proto_fn; sbuf->ops = &raw_sbufio_ops; } /* got new socket from accept() */ bool sbuf_accept(SBuf *sbuf, int sock, bool is_unix) { bool res; Assert(iobuf_empty(sbuf->io) && sbuf->sock == 0); AssertSanity(sbuf); sbuf->sock = sock; if (!tune_socket(sock, is_unix)) goto failed; if (!cf_reboot) { res = sbuf_wait_for_data(sbuf); if (!res) goto failed; /* socket should already have some data (linux only) */ if (cf_tcp_defer_accept && !is_unix) { sbuf_main_loop(sbuf, DO_RECV); if (!sbuf->sock) return false; } } return true; failed: sbuf_call_proto(sbuf, SBUF_EV_RECV_FAILED); return false; } /* need to connect() to get a socket */ bool sbuf_connect(SBuf *sbuf, const struct sockaddr *sa, int sa_len, int timeout_sec) { int res, sock; struct timeval timeout; bool is_unix = sa->sa_family == AF_UNIX; Assert(iobuf_empty(sbuf->io) && sbuf->sock == 0); AssertSanity(sbuf); /* * common stuff */ sock = socket(sa->sa_family, SOCK_STREAM, 0); if (sock < 0) { /* probably fd limit */ goto failed; } if (!tune_socket(sock, is_unix)) goto failed; sbuf->sock = sock; timeout.tv_sec = timeout_sec; timeout.tv_usec = 0; /* launch connection */ res = safe_connect(sock, sa, sa_len); if (res == 0) { /* unix socket gives connection immediately */ sbuf_connect_cb(sock, EV_WRITE, sbuf); return true; } else if (errno == EINPROGRESS) { /* tcp socket needs waiting */ event_set(&sbuf->ev, sock, EV_WRITE, sbuf_connect_cb, sbuf); res = event_add(&sbuf->ev, &timeout); if (res >= 0) { sbuf->wait_type = W_CONNECT; return true; } } failed: log_warning("sbuf_connect failed: %s", strerror(errno)); if (sock >= 0) safe_close(sock); sbuf->sock = 0; sbuf_call_proto(sbuf, SBUF_EV_CONNECT_FAILED); return false; } /* don't wait for data on this socket */ bool sbuf_pause(SBuf *sbuf) { AssertActive(sbuf); Assert(sbuf->wait_type == W_RECV); if (event_del(&sbuf->ev) < 0) { log_warning("event_del: %s", strerror(errno)); return false; } sbuf->wait_type = W_NONE; return true; } /* resume from pause, start waiting for data */ void sbuf_continue(SBuf *sbuf) { bool do_recv = DO_RECV; bool res; AssertActive(sbuf); res = sbuf_wait_for_data(sbuf); if (!res) { /* drop if problems */ sbuf_call_proto(sbuf, SBUF_EV_RECV_FAILED); return; } /* * It's tempting to try to avoid the recv() but that would * only work if no code wants to see full packet. * * This is not true in ServerParameter case. */ /* * if (sbuf->recv_pos - sbuf->pkt_pos >= SBUF_SMALL_PKT) * do_recv = false; */ sbuf_main_loop(sbuf, do_recv); } /* * Resume from pause and give socket over to external * callback function. * * The callback will be called with arg given to sbuf_init. */ bool sbuf_continue_with_callback(SBuf *sbuf, sbuf_libevent_cb user_cb) { int err; AssertActive(sbuf); event_set(&sbuf->ev, sbuf->sock, EV_READ | EV_PERSIST, user_cb, sbuf); err = event_add(&sbuf->ev, NULL); if (err < 0) { log_warning("sbuf_continue_with_callback: %s", strerror(errno)); return false; } sbuf->wait_type = W_RECV; return true; } bool sbuf_use_callback_once(SBuf *sbuf, short ev, sbuf_libevent_cb user_cb) { int err; AssertActive(sbuf); if (sbuf->wait_type != W_NONE) { err = event_del(&sbuf->ev); sbuf->wait_type = W_NONE; /* make sure its called only once */ if (err < 0) { log_warning("sbuf_queue_once: event_del failed: %s", strerror(errno)); return false; } } /* setup one one-off event handler */ event_set(&sbuf->ev, sbuf->sock, ev, user_cb, sbuf); err = event_add(&sbuf->ev, NULL); if (err < 0) { log_warning("sbuf_queue_once: event_add failed: %s", strerror(errno)); return false; } sbuf->wait_type = W_ONCE; return true; } /* socket cleanup & close: keeps .handler and .arg values */ bool sbuf_close(SBuf *sbuf) { if (sbuf->wait_type) { Assert(sbuf->sock); /* event_del() acts funny occasionally, debug it */ errno = 0; if (event_del(&sbuf->ev) < 0) { if (errno) { log_warning("event_del: %s", strerror(errno)); } else { log_warning("event_del: libevent error"); } /* we can retry whole sbuf_close() if needed */ /* if (errno == ENOMEM) return false; */ } } sbuf_op_close(sbuf); sbuf->dst = NULL; sbuf->sock = 0; sbuf->pkt_remain = 0; sbuf->pkt_action = sbuf->wait_type = 0; if (sbuf->io) { slab_free(iobuf_cache, sbuf->io); sbuf->io = NULL; } return true; } /* proto_fn tells to send some bytes to socket */ void sbuf_prepare_send(SBuf *sbuf, SBuf *dst, unsigned amount) { AssertActive(sbuf); Assert(sbuf->pkt_remain == 0); /* Assert(sbuf->pkt_action == ACT_UNSET || sbuf->pkt_action == ACT_SEND || iobuf_amount_pending(&sbuf->io)); */ Assert(amount > 0); sbuf->pkt_action = ACT_SEND; sbuf->pkt_remain = amount; sbuf->dst = dst; } /* proto_fn tells to skip some amount of bytes */ void sbuf_prepare_skip(SBuf *sbuf, unsigned amount) { AssertActive(sbuf); Assert(sbuf->pkt_remain == 0); /* Assert(sbuf->pkt_action == ACT_UNSET || iobuf_send_pending_avail(&sbuf->io)); */ Assert(amount > 0); sbuf->pkt_action = ACT_SKIP; sbuf->pkt_remain = amount; } /* proto_fn tells to skip some amount of bytes */ void sbuf_prepare_fetch(SBuf *sbuf, unsigned amount) { AssertActive(sbuf); Assert(sbuf->pkt_remain == 0); /* Assert(sbuf->pkt_action == ACT_UNSET || iobuf_send_pending_avail(&sbuf->io)); */ Assert(amount > 0); sbuf->pkt_action = ACT_CALL; sbuf->pkt_remain = amount; /* sbuf->dst = NULL; // FIXME ?? */ } /************************* * Internal functions *************************/ /* * Call proto callback with proper struct MBuf. * * If callback returns true it used one of sbuf_prepare_* on sbuf, * and processing can continue. * * If it returned false it used sbuf_pause(), sbuf_close() or simply * wants to wait for next event loop (e.g. too few data available). * Callee should not touch sbuf in that case and just return to libevent. */ static bool sbuf_call_proto(SBuf *sbuf, int event) { struct MBuf mbuf; IOBuf *io = sbuf->io; bool res; AssertSanity(sbuf); Assert(event != SBUF_EV_READ || iobuf_amount_parse(io) > 0); /* if pkt callback, limit only with current packet */ if (event == SBUF_EV_PKT_CALLBACK) { iobuf_parse_limit(io, &mbuf, sbuf->pkt_remain); } else if (event == SBUF_EV_READ) { iobuf_parse_all(io, &mbuf); } else { memset(&mbuf, 0, sizeof(mbuf)); } res = sbuf->proto_cb(sbuf, event, &mbuf); AssertSanity(sbuf); Assert(event != SBUF_EV_READ || !res || sbuf->sock > 0); return res; } /* let's wait for new data */ static bool sbuf_wait_for_data(SBuf *sbuf) { int err; event_set(&sbuf->ev, sbuf->sock, EV_READ | EV_PERSIST, sbuf_recv_cb, sbuf); err = event_add(&sbuf->ev, NULL); if (err < 0) { log_warning("sbuf_wait_for_data: event_add failed: %s", strerror(errno)); return false; } sbuf->wait_type = W_RECV; return true; } static void sbuf_recv_forced_cb(int sock, short flags, void *arg) { SBuf *sbuf = arg; sbuf->wait_type = W_NONE; if (sbuf_wait_for_data(sbuf)) { sbuf_recv_cb(sock, flags, arg); } else { sbuf_call_proto(sbuf, SBUF_EV_RECV_FAILED); } } static bool sbuf_wait_for_data_forced(SBuf *sbuf) { int err; struct timeval tv_min; tv_min.tv_sec = 0; tv_min.tv_usec = 1; if (sbuf->wait_type != W_NONE) { event_del(&sbuf->ev); sbuf->wait_type = W_NONE; } event_set(&sbuf->ev, sbuf->sock, EV_READ, sbuf_recv_forced_cb, sbuf); err = event_add(&sbuf->ev, &tv_min); if (err < 0) { log_warning("sbuf_wait_for_data: event_add failed: %s", strerror(errno)); return false; } sbuf->wait_type = W_ONCE; return true; } /* libevent EV_WRITE: called when dest socket is writable again */ static void sbuf_send_cb(int sock, short flags, void *arg) { SBuf *sbuf = arg; bool res; /* sbuf was closed before in this loop */ if (!sbuf->sock) return; AssertSanity(sbuf); Assert(sbuf->wait_type == W_SEND); sbuf->wait_type = W_NONE; /* prepare normal situation for sbuf_main_loop */ res = sbuf_wait_for_data(sbuf); if (res) { /* here we should certainly skip recv() */ sbuf_main_loop(sbuf, SKIP_RECV); } else { /* drop if problems */ sbuf_call_proto(sbuf, SBUF_EV_SEND_FAILED); } } /* socket is full, wait until it's writable again */ static bool sbuf_queue_send(SBuf *sbuf) { int err; AssertActive(sbuf); Assert(sbuf->wait_type == W_RECV); /* if false is returned, the socket will be closed later */ /* stop waiting for read events */ err = event_del(&sbuf->ev); sbuf->wait_type = W_NONE; /* make sure its called only once */ if (err < 0) { log_warning("sbuf_queue_send: event_del failed: %s", strerror(errno)); return false; } /* instead wait for EV_WRITE on destination socket */ event_set(&sbuf->ev, sbuf->dst->sock, EV_WRITE, sbuf_send_cb, sbuf); err = event_add(&sbuf->ev, NULL); if (err < 0) { log_warning("sbuf_queue_send: event_add failed: %s", strerror(errno)); return false; } sbuf->wait_type = W_SEND; return true; } /* * There's data in buffer to be sent. Returns bool if processing can continue. * * Does not look at pkt_pos/remain fields, expects them to be merged to send_* */ static bool sbuf_send_pending(SBuf *sbuf) { int res, avail; IOBuf *io = sbuf->io; AssertActive(sbuf); Assert(sbuf->dst || iobuf_amount_pending(io) == 0); try_more: /* how much data is available for sending */ avail = iobuf_amount_pending(io); if (avail == 0) return true; if (sbuf->dst->sock == 0) { log_error("sbuf_send_pending: no dst sock?"); return false; } /* actually send it */ //res = iobuf_send_pending(io, sbuf->dst->sock); res = sbuf_op_send(sbuf->dst, io->buf + io->done_pos, avail); if (res > 0) { io->done_pos += res; } else if (res < 0) { if (errno == EAGAIN) { if (!sbuf_queue_send(sbuf)) /* drop if queue failed */ sbuf_call_proto(sbuf, SBUF_EV_SEND_FAILED); } else { sbuf_call_proto(sbuf, SBUF_EV_SEND_FAILED); } return false; } AssertActive(sbuf); /* * Should do sbuf_queue_send() immediately? * * To be sure, let's run into EAGAIN. */ goto try_more; } /* process as much data as possible */ static bool sbuf_process_pending(SBuf *sbuf) { unsigned avail; IOBuf *io = sbuf->io; bool full = iobuf_amount_recv(io) <= 0; bool res; while (1) { AssertActive(sbuf); /* * Enough for now? * * The (avail <= SBUF_SMALL_PKT) check is to avoid partial pkts. * As SBuf should not assume knowledge about packets, * the check is not done in !full case. Packet handler can * then still notify about partial packet by returning false. */ avail = iobuf_amount_parse(io); if (avail == 0 || (full && avail <= SBUF_SMALL_PKT)) break; /* * If start of packet, process packet header. */ if (sbuf->pkt_remain == 0) { res = sbuf_call_proto(sbuf, SBUF_EV_READ); if (!res) return false; Assert(sbuf->pkt_remain > 0); } if (sbuf->pkt_action == ACT_SKIP || sbuf->pkt_action == ACT_CALL) { /* send any pending data before skipping */ if (iobuf_amount_pending(io) > 0) { res = sbuf_send_pending(sbuf); if (!res) return res; } } if (avail > sbuf->pkt_remain) avail = sbuf->pkt_remain; switch (sbuf->pkt_action) { case ACT_SEND: iobuf_tag_send(io, avail); break; case ACT_CALL: res = sbuf_call_proto(sbuf, SBUF_EV_PKT_CALLBACK); if (!res) return false; /* after callback, skip pkt */ case ACT_SKIP: iobuf_tag_skip(io, avail); break; } sbuf->pkt_remain -= avail; } return sbuf_send_pending(sbuf); } /* reposition at buffer start again */ static void sbuf_try_resync(SBuf *sbuf, bool release) { IOBuf *io = sbuf->io; if (io) { log_noise("resync: done=%d, parse=%d, recv=%d", io->done_pos, io->parse_pos, io->recv_pos); } AssertActive(sbuf); if (!io) return; if (release && iobuf_empty(io)) { slab_free(iobuf_cache, io); sbuf->io = NULL; } else { iobuf_try_resync(io, SBUF_SMALL_PKT); } } /* actually ask kernel for more data */ static bool sbuf_actual_recv(SBuf *sbuf, unsigned len) { int got; IOBuf *io = sbuf->io; uint8_t *dst = io->buf + io->recv_pos; unsigned avail = iobuf_amount_recv(io); if (len > avail) len = avail; got = sbuf_op_recv(sbuf, dst, len); if (got > 0) { io->recv_pos += got; } else if (got == 0) { /* eof from socket */ sbuf_call_proto(sbuf, SBUF_EV_RECV_FAILED); return false; } else if (got < 0 && errno != EAGAIN) { /* some error occurred */ sbuf_call_proto(sbuf, SBUF_EV_RECV_FAILED); return false; } return true; } /* callback for libevent EV_READ */ static void sbuf_recv_cb(int sock, short flags, void *arg) { SBuf *sbuf = arg; sbuf_main_loop(sbuf, DO_RECV); } static bool allocate_iobuf(SBuf *sbuf) { if (sbuf->io == NULL) { sbuf->io = slab_alloc(iobuf_cache); if (sbuf->io == NULL) { sbuf_call_proto(sbuf, SBUF_EV_RECV_FAILED); return false; } iobuf_reset(sbuf->io); } return true; } /* * Main recv-parse-send-repeat loop. * * Reason for skip_recv is to avoid extra recv(). The problem with it * is EOF from socket. Currently that means that the pending data is * dropped. Fortunately server sockets are not paused and dropping * data from client is no problem. So only place where skip_recv is * important is sbuf_send_cb(). */ static void sbuf_main_loop(SBuf *sbuf, bool skip_recv) { unsigned free, ok; int loopcnt = 0; /* sbuf was closed before in this event loop */ if (!sbuf->sock) return; /* reading should be disabled when waiting */ Assert(sbuf->wait_type == W_RECV); AssertSanity(sbuf); if (!allocate_iobuf(sbuf)) return; /* avoid recv() if asked */ if (skip_recv) goto skip_recv; try_more: /* make room in buffer */ sbuf_try_resync(sbuf, false); /* avoid spending too much time on single socket */ if (cf_sbuf_loopcnt > 0 && loopcnt >= cf_sbuf_loopcnt) { log_debug("loopcnt full"); /* * sbuf_process_pending() avoids some data if buffer is full, * but as we exit processing loop here, we need to retry * after resync to process all data. (result is ignored) */ ok = sbuf_process_pending(sbuf); sbuf_wait_for_data_forced(sbuf); return; } loopcnt++; /* * here used to be if (free > SBUF_SMALL_PKT) check * but with skip_recv switch its should not be needed anymore. */ free = iobuf_amount_recv(sbuf->io); if (free > 0) { /* * When suspending, try to hit packet boundary ASAP. */ if (cf_pause_mode == P_SUSPEND && sbuf->pkt_remain > 0 && sbuf->pkt_remain < free) { free = sbuf->pkt_remain; } /* now fetch the data */ ok = sbuf_actual_recv(sbuf, free); if (!ok) return; } skip_recv: /* now handle it */ ok = sbuf_process_pending(sbuf); if (!ok) return; /* if the buffer is full, there can be more data available */ if (iobuf_amount_recv(sbuf->io) <= 0) goto try_more; /* clean buffer */ sbuf_try_resync(sbuf, true); /* notify proto that all is sent */ if (sbuf_is_empty(sbuf)) sbuf_call_proto(sbuf, SBUF_EV_FLUSH); if (sbuf->tls_state == SBUF_TLS_DO_HANDSHAKE) { sbuf->pkt_action = SBUF_TLS_IN_HANDSHAKE; handle_tls_handshake(sbuf); } } /* check if there is any error pending on socket */ static bool sbuf_after_connect_check(SBuf *sbuf) { int optval = 0, err; socklen_t optlen = sizeof(optval); err = getsockopt(sbuf->sock, SOL_SOCKET, SO_ERROR, (void*)&optval, &optlen); if (err < 0) { log_debug("sbuf_after_connect_check: getsockopt: %s", strerror(errno)); return false; } if (optval != 0) { log_debug("sbuf_after_connect_check: pending error: %s", strerror(optval)); return false; } return true; } /* callback for libevent EV_WRITE when connecting */ static void sbuf_connect_cb(int sock, short flags, void *arg) { SBuf *sbuf = arg; Assert(sbuf->wait_type == W_CONNECT || sbuf->wait_type == W_NONE); sbuf->wait_type = W_NONE; if (flags & EV_WRITE) { if (!sbuf_after_connect_check(sbuf)) goto failed; if (!sbuf_call_proto(sbuf, SBUF_EV_CONNECT_OK)) return; if (!sbuf_wait_for_data(sbuf)) goto failed; return; } failed: sbuf_call_proto(sbuf, SBUF_EV_CONNECT_FAILED); } /* send some data to listening socket */ bool sbuf_answer(SBuf *sbuf, const void *buf, unsigned len) { int res; if (sbuf->sock <= 0) return false; res = sbuf_op_send(sbuf, buf, len); if (res < 0) { log_debug("sbuf_answer: error sending: %s", strerror(errno)); } else if ((unsigned)res != len) { log_debug("sbuf_answer: partial send: len=%d sent=%d", len, res); } return (unsigned)res == len; } /* * Standard IO ops. */ static int raw_sbufio_recv(struct SBuf *sbuf, void *dst, unsigned int len) { return safe_recv(sbuf->sock, dst, len, 0); } static int raw_sbufio_send(struct SBuf *sbuf, const void *data, unsigned int len) { return safe_send(sbuf->sock, data, len, 0); } static int raw_sbufio_close(struct SBuf *sbuf) { if (sbuf->sock > 0) { safe_close(sbuf->sock); sbuf->sock = 0; } return 0; } /* * TLS support. */ #ifdef USE_TLS static struct tls_config *client_accept_conf; static struct tls_config *server_connect_conf; static struct tls *client_accept_base; /* * TLS setup */ static void setup_tls(struct tls_config *conf, const char *pfx, int sslmode, const char *protocols, const char *ciphers, const char *keyfile, const char *certfile, const char *cafile, const char *dheparams, const char *ecdhecurve, bool does_connect) { int err; if (*protocols) { uint32_t protos = TLS_PROTOCOLS_ALL; err = tls_config_parse_protocols(&protos, protocols); if (err) { log_error("Invalid %s_protocols: %s", pfx, protocols); } else { tls_config_set_protocols(conf, protos); } } if (*ciphers) { err = tls_config_set_ciphers(conf, ciphers); if (err) log_error("Invalid %s_ciphers: %s", pfx, ciphers); } if (*dheparams) { err = tls_config_set_dheparams(conf, dheparams); if (err) log_error("Invalid %s_dheparams: %s", pfx, dheparams); } if (*ecdhecurve) { err = tls_config_set_ecdhecurve(conf, ecdhecurve); if (err) log_error("Invalid %s_ecdhecurve: %s", pfx, ecdhecurve); } if (*cafile) { err = tls_config_set_ca_file(conf, cafile); if (err) log_error("Invalid %s_ca_file: %s", pfx, cafile); } if (*keyfile) { err = tls_config_set_key_file(conf, keyfile); if (err) log_error("Invalid %s_key_file: %s", pfx, keyfile); } if (*certfile) { err = tls_config_set_cert_file(conf, certfile); if (err) log_error("Invalid %s_cert_file: %s", pfx, certfile); } if (does_connect) { /* TLS client, check server? */ if (sslmode == SSLMODE_VERIFY_FULL) { tls_config_verify(conf); } else if (sslmode == SSLMODE_VERIFY_CA) { tls_config_verify(conf); tls_config_insecure_noverifyname(conf); } else { tls_config_insecure_noverifycert(conf); tls_config_insecure_noverifyname(conf); } } else { /* TLS server, check client? */ if (sslmode == SSLMODE_VERIFY_FULL) { tls_config_verify_client_optional(conf); } else if (sslmode == SSLMODE_VERIFY_CA) { tls_config_verify_client_optional(conf); } } } void sbuf_tls_setup(void) { int err; if (cf_client_tls_sslmode != SSLMODE_DISABLED) { if (!*cf_client_tls_key_file || !*cf_client_tls_cert_file) die("To allow TLS connections from clients, client_tls_key_file and client_tls_cert_file must be set."); } if (cf_auth_type == AUTH_CERT) { if (cf_client_tls_sslmode != SSLMODE_VERIFY_FULL) die("auth_type=cert requires client_tls_sslmode=SSLMODE_VERIFY_FULL"); if (*cf_client_tls_ca_file == '\0') die("auth_type=cert requires client_tls_ca_file"); } else if (cf_client_tls_sslmode > SSLMODE_VERIFY_CA && *cf_client_tls_ca_file == '\0') { die("client_tls_sslmode requires client_tls_ca_file"); } err = tls_init(); if (err) fatal("tls_init failed"); if (cf_server_tls_sslmode != SSLMODE_DISABLED) { server_connect_conf = tls_config_new(); if (!server_connect_conf) die("tls_config_new failed 1"); setup_tls(server_connect_conf, "server_tls", cf_server_tls_sslmode, cf_server_tls_protocols, cf_server_tls_ciphers, cf_server_tls_key_file, cf_server_tls_cert_file, cf_server_tls_ca_file, "", "", true); } if (cf_client_tls_sslmode != SSLMODE_DISABLED) { client_accept_conf = tls_config_new(); if (!client_accept_conf) die("tls_config_new failed 2"); setup_tls(client_accept_conf, "client_tls", cf_client_tls_sslmode, cf_client_tls_protocols, cf_client_tls_ciphers, cf_client_tls_key_file, cf_client_tls_cert_file, cf_client_tls_ca_file, cf_client_tls_dheparams, cf_client_tls_ecdhecurve, false); client_accept_base = tls_server(); if (!client_accept_base) die("server_base failed"); err = tls_configure(client_accept_base, client_accept_conf); if (err) die("TLS setup failed: %s", tls_error(client_accept_base)); } } /* * TLS handshake */ static bool handle_tls_handshake(SBuf *sbuf) { int err; err = tls_handshake(sbuf->tls); log_noise("tls_handshake: err=%d", err); if (err == TLS_WANT_POLLIN) { return sbuf_use_callback_once(sbuf, EV_READ, sbuf_tls_handshake_cb); } else if (err == TLS_WANT_POLLOUT) { return sbuf_use_callback_once(sbuf, EV_WRITE, sbuf_tls_handshake_cb); } else if (err == 0) { sbuf->tls_state = SBUF_TLS_OK; sbuf_call_proto(sbuf, SBUF_EV_TLS_READY); return true; } else { log_warning("TLS handshake error: %s", tls_error(sbuf->tls)); return false; } } static void sbuf_tls_handshake_cb(int fd, short flags, void *_sbuf) { SBuf *sbuf = _sbuf; sbuf->wait_type = W_NONE; if (!handle_tls_handshake(sbuf)) sbuf_call_proto(sbuf, SBUF_EV_RECV_FAILED); } /* * Accept TLS connection. */ bool sbuf_tls_accept(SBuf *sbuf) { int err; if (!sbuf_pause(sbuf)) return false; sbuf->ops = &tls_sbufio_ops; err = tls_accept_fds(client_accept_base, &sbuf->tls, sbuf->sock, sbuf->sock); log_noise("tls_accept_fds: err=%d", err); if (err < 0) { log_warning("TLS accept error: %s", tls_error(sbuf->tls)); return false; } sbuf->tls_state = SBUF_TLS_DO_HANDSHAKE; return true; } /* * Connect to remote TLS host. */ bool sbuf_tls_connect(SBuf *sbuf, const char *hostname) { struct tls *ctls; int err; if (!sbuf_pause(sbuf)) return false; if (cf_server_tls_sslmode != SSLMODE_VERIFY_FULL) hostname = NULL; ctls = tls_client(); if (!ctls) return false; err = tls_configure(ctls, server_connect_conf); if (err < 0) { log_error("tls client config failed: %s", tls_error(ctls)); tls_free(ctls); return false; } sbuf->tls = ctls; sbuf->tls_host = hostname; sbuf->ops = &tls_sbufio_ops; err = tls_connect_fds(sbuf->tls, sbuf->sock, sbuf->sock, sbuf->tls_host); if (err < 0) { log_warning("TLS connect error: %s", tls_error(sbuf->tls)); return false; } sbuf->tls_state = SBUF_TLS_DO_HANDSHAKE; return true; } /* * TLS IO ops. */ static int tls_sbufio_recv(struct SBuf *sbuf, void *dst, unsigned int len) { ssize_t out = 0; if (sbuf->tls_state != SBUF_TLS_OK) { errno = EIO; return -1; } out = tls_read(sbuf->tls, dst, len); log_noise("tls_read: req=%u out=%d", len, (int)out); if (out >= 0) { return out; } else if (out == TLS_WANT_POLLIN) { errno = EAGAIN; } else if (out == TLS_WANT_POLLOUT) { log_warning("tls_sbufio_recv: got TLS_WANT_POLLOUT"); errno = EIO; } else { log_warning("tls_sbufio_recv: %s", tls_error(sbuf->tls)); errno = EIO; } return -1; } static int tls_sbufio_send(struct SBuf *sbuf, const void *data, unsigned int len) { ssize_t out; if (sbuf->tls_state != SBUF_TLS_OK) { errno = EIO; return -1; } out = tls_write(sbuf->tls, data, len); log_noise("tls_write: req=%u out=%d", len, (int)out); if (out >= 0) { return out; } else if (out == TLS_WANT_POLLOUT) { errno = EAGAIN; } else if (out == TLS_WANT_POLLIN) { log_warning("tls_sbufio_send: got TLS_WANT_POLLIN"); errno = EIO; } else { log_warning("tls_sbufio_send: %s", tls_error(sbuf->tls)); errno = EIO; } return -1; } static int tls_sbufio_close(struct SBuf *sbuf) { log_noise("tls_close"); if (sbuf->tls) { tls_close(sbuf->tls); tls_free(sbuf->tls); sbuf->tls = NULL; } if (sbuf->sock > 0) { safe_close(sbuf->sock); sbuf->sock = 0; } return 0; } #else void sbuf_tls_setup(void) { } bool sbuf_tls_accept(SBuf *sbuf) { return false; } bool sbuf_tls_connect(SBuf *sbuf, const char *hostname) { return false; } #endif pgbouncer-1.7/src/admin.c0000664000175000017500000011404712572127367012336 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Admin console commands. */ #include "bouncer.h" #include #include #include /* regex elements */ #define WS0 "[ \t\n\r]*" #define WS1 "[ \t\n\r]+" #define WORD "(\"([^\"]+|\"\")*\"|[0-9a-z_]+)" #define STRING "('([^']|'')*')" /* possible max + 1 */ #define MAX_GROUPS 10 /* group numbers */ #define CMD_NAME 1 #define CMD_ARG 4 #define SET_KEY 1 #define SET_VAL 4 typedef bool (*cmd_func_t)(PgSocket *admin, const char *arg); struct cmd_lookup { const char *word; cmd_func_t func; }; /* CMD [arg]; */ static const char cmd_normal_rx[] = "^" WS0 WORD "(" WS1 WORD ")?" WS0 "(;" WS0 ")?$"; /* SET with simple value */ static const char cmd_set_word_rx[] = "^" WS0 "set" WS1 WORD WS0 "(=|to)" WS0 WORD WS0 "(;" WS0 ")?$"; /* SET with quoted value */ static const char cmd_set_str_rx[] = "^" WS0 "set" WS1 WORD WS0 "(=|to)" WS0 STRING WS0 "(;" WS0 ")?$"; /* compiled regexes */ static regex_t rc_cmd; static regex_t rc_set_word; static regex_t rc_set_str; static PgPool *admin_pool; /* only valid during processing */ static const char *current_query; static bool syntax_error(PgSocket *admin) { return admin_error(admin, "invalid command '%s', use SHOW HELP;", current_query ? current_query : ""); } static bool exec_cmd(struct cmd_lookup *lookup, PgSocket *admin, const char *cmd, const char *arg) { for (; lookup->word; lookup++) { if (strcasecmp(lookup->word, cmd) == 0) return lookup->func(admin, arg); } return syntax_error(admin); } bool admin_error(PgSocket *admin, const char *fmt, ...) { char str[1024]; va_list ap; bool res = true; va_start(ap, fmt); vsnprintf(str, sizeof(str), fmt, ap); va_end(ap); log_error("%s", str); if (admin) res = send_pooler_error(admin, true, str); return res; } static int count_paused_databases(void) { struct List *item; PgDatabase *db; int cnt = 0; statlist_for_each(item, &database_list) { db = container_of(item, PgDatabase, head); cnt += db->db_paused; } return cnt; } static int count_db_active(PgDatabase *db) { struct List *item; PgPool *pool; int cnt = 0; statlist_for_each(item, &pool_list) { pool = container_of(item, PgPool, head); if (pool->db != db) continue; cnt += pool_server_count(pool); } return cnt; } bool admin_flush(PgSocket *admin, PktBuf *buf, const char *desc) { pktbuf_write_CommandComplete(buf, desc); pktbuf_write_ReadyForQuery(buf); return pktbuf_send_queued(buf, admin); } bool admin_ready(PgSocket *admin, const char *desc) { PktBuf buf; uint8_t tmp[512]; pktbuf_static(&buf, tmp, sizeof(tmp)); pktbuf_write_CommandComplete(&buf, desc); pktbuf_write_ReadyForQuery(&buf); return pktbuf_send_immediate(&buf, admin); } /* * some silly clients start actively messing with server parameters * without checking if thats necessary. Fake some env for them. */ struct FakeParam { const char *name; const char *value; }; static const struct FakeParam fake_param_list[] = { { "client_encoding", "UTF-8" }, { "default_transaction_isolation", "read committed" }, { "standard_conforming_strings", "on" }, { "datestyle", "ISO" }, { "timezone", "GMT" }, { NULL }, }; /* fake result send, returns if handled */ static bool fake_show(PgSocket *admin, const char *name) { PktBuf *buf; const struct FakeParam *p; bool got = false; for (p = fake_param_list; p->name; p++) { if (strcasecmp(name, p->name) == 0) { got = true; break; } } if (got) { buf = pktbuf_dynamic(256); if (buf) { pktbuf_write_RowDescription(buf, "s", p->name); pktbuf_write_DataRow(buf, "s", p->value); admin_flush(admin, buf, "SHOW"); } else admin_error(admin, "no mem"); } return got; } static bool fake_set(PgSocket *admin, const char *key, const char *val) { PktBuf *buf; const struct FakeParam *p; bool got = false; for (p = fake_param_list; p->name; p++) { if (strcasecmp(key, p->name) == 0) { got = true; break; } } if (got) { buf = pktbuf_dynamic(256); if (buf) { pktbuf_write_Notice(buf, "SET ignored"); admin_flush(admin, buf, "SET"); } else admin_error(admin, "no mem"); } return got; } /* Command: SET key = val; */ static bool admin_set(PgSocket *admin, const char *key, const char *val) { char tmp[512]; bool ok; if (fake_set(admin, key, val)) return true; if (admin->admin_user) { ok = set_config_param(key, val); if (ok) { snprintf(tmp, sizeof(tmp), "SET %s=%s", key, val); return admin_ready(admin, tmp); } else { return admin_error(admin, "SET failed"); } } else return admin_error(admin, "admin access needed"); } /* send a row with sendmsg, optionally attaching a fd */ static bool send_one_fd(PgSocket *admin, int fd, const char *task, const char *user, const char *db, const char *addr, int port, uint64_t ckey, int link, const char *client_enc, const char *std_strings, const char *datestyle, const char *timezone, const char *password) { struct msghdr msg; struct cmsghdr *cmsg; struct iovec iovec; int res; uint8_t cntbuf[CMSG_SPACE(sizeof(int))]; struct PktBuf *pkt = pktbuf_temp(); pktbuf_write_DataRow(pkt, "issssiqisssss", fd, task, user, db, addr, port, ckey, link, client_enc, std_strings, datestyle, timezone, password); if (pkt->failed) return false; iovec.iov_base = pkt->buf; iovec.iov_len = pktbuf_written(pkt); /* sending fds */ memset(&msg, 0, sizeof(msg)); msg.msg_iov = &iovec; msg.msg_iovlen = 1; /* attach a fd */ if (pga_is_unix(&admin->remote_addr) && admin->own_user && !admin->sbuf.tls) { msg.msg_control = cntbuf; msg.msg_controllen = sizeof(cntbuf); cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = CMSG_LEN(sizeof(int)); memcpy(CMSG_DATA(cmsg), &fd, sizeof(int)); msg.msg_controllen = cmsg->cmsg_len; } slog_debug(admin, "sending socket list: fd=%d, len=%d", fd, (int)msg.msg_controllen); if (msg.msg_controllen) { res = safe_sendmsg(sbuf_socket(&admin->sbuf), &msg, 0); } else { res = sbuf_op_send(&admin->sbuf, pkt->buf, pktbuf_written(pkt)); } if (res < 0) { log_error("send_one_fd: sendmsg error: %s", strerror(errno)); return false; } else if ((size_t)res != iovec.iov_len) { log_error("send_one_fd: partial sendmsg"); return false; } return true; } /* send a row with sendmsg, optionally attaching a fd */ static bool show_one_fd(PgSocket *admin, PgSocket *sk) { PgAddr *addr = &sk->remote_addr; struct MBuf tmp; VarCache *v = &sk->vars; uint64_t ckey; const struct PStr *client_encoding = v->var_list[VClientEncoding]; const struct PStr *std_strings = v->var_list[VStdStr]; const struct PStr *datestyle = v->var_list[VDateStyle]; const struct PStr *timezone = v->var_list[VTimeZone]; char addrbuf[PGADDR_BUF]; const char *password = NULL; /* Skip TLS sockets */ if (sk->sbuf.tls || (sk->link && sk->link->sbuf.tls)) return true; mbuf_init_fixed_reader(&tmp, sk->cancel_key, 8); if (!mbuf_get_uint64be(&tmp, &ckey)) return false; if (sk->pool->db->auth_user && sk->auth_user && !find_user(sk->auth_user->name)) password = sk->auth_user->passwd; return send_one_fd(admin, sbuf_socket(&sk->sbuf), is_server_socket(sk) ? "server" : "client", sk->auth_user ? sk->auth_user->name : NULL, sk->pool ? sk->pool->db->name : NULL, pga_ntop(addr, addrbuf, sizeof(addrbuf)), pga_port(addr), ckey, sk->link ? sbuf_socket(&sk->link->sbuf) : 0, client_encoding ? client_encoding->str : NULL, std_strings ? std_strings->str : NULL, datestyle ? datestyle->str : NULL, timezone ? timezone->str : NULL, password); } static bool show_pooler_cb(void *arg, int fd, const PgAddr *a) { char buf[PGADDR_BUF]; return send_one_fd(arg, fd, "pooler", NULL, NULL, pga_ntop(a, buf, sizeof(buf)), pga_port(a), 0, 0, NULL, NULL, NULL, NULL, NULL); } /* send a row with sendmsg, optionally attaching a fd */ static bool show_pooler_fds(PgSocket *admin) { return for_each_pooler_fd(show_pooler_cb, admin); } static bool show_fds_from_list(PgSocket *admin, struct StatList *list) { struct List *item; PgSocket *sk; bool res = true; statlist_for_each(item, list) { sk = container_of(item, PgSocket, head); res = show_one_fd(admin, sk); if (!res) break; } return res; } static PgDatabase *find_or_register_database(PgSocket *admin, const char *name) { PgDatabase *db = find_database(name); if (db == NULL) { db = register_auto_database(name); if (db != NULL) { slog_info(admin, "registered new auto-database: %s", name); } } return db; } /* * Command: SHOW FDS * * If privileged connection, send also actual fds */ static bool admin_show_fds(PgSocket *admin, const char *arg) { struct List *item; PgPool *pool; bool res; /* * Dangerous to show to everybody: * - can lock pooler as code flips async option * - show cancel keys for all users * - shows passwords (md5) for dynamic users */ if (!admin->admin_user) return admin_error(admin, "admin access needed"); /* * It's very hard to send it reliably over in async manner, * so turn async off for this resultset. */ socket_set_nonblocking(sbuf_socket(&admin->sbuf), 0); /* * send resultset */ SEND_RowDescription(res, admin, "issssiqisssss", "fd", "task", "user", "database", "addr", "port", "cancel", "link", "client_encoding", "std_strings", "datestyle", "timezone", "password"); if (res) res = show_pooler_fds(admin); if (res) res = show_fds_from_list(admin, &login_client_list); statlist_for_each(item, &pool_list) { pool = container_of(item, PgPool, head); if (pool->db->admin) continue; res = res && show_fds_from_list(admin, &pool->active_client_list); res = res && show_fds_from_list(admin, &pool->waiting_client_list); res = res && show_fds_from_list(admin, &pool->active_server_list); res = res && show_fds_from_list(admin, &pool->idle_server_list); res = res && show_fds_from_list(admin, &pool->used_server_list); res = res && show_fds_from_list(admin, &pool->tested_server_list); res = res && show_fds_from_list(admin, &pool->new_server_list); if (!res) break; } if (res) res = admin_ready(admin, "SHOW"); /* turn async back on */ socket_set_nonblocking(sbuf_socket(&admin->sbuf), 1); return res; } /* Command: SHOW DATABASES */ static bool admin_show_databases(PgSocket *admin, const char *arg) { PgDatabase *db; struct List *item; const char *f_user; PktBuf *buf; struct CfValue cv; const char *pool_mode_str; cv.extra = pool_mode_map; buf = pktbuf_dynamic(256); if (!buf) { admin_error(admin, "no mem"); return true; } pktbuf_write_RowDescription(buf, "ssissiisii", "name", "host", "port", "database", "force_user", "pool_size", "reserve_pool", "pool_mode", "max_connections", "current_connections"); statlist_for_each(item, &database_list) { db = container_of(item, PgDatabase, head); f_user = db->forced_user ? db->forced_user->name : NULL; pool_mode_str = NULL; cv.value_p = &db->pool_mode; if (db->pool_mode != POOL_INHERIT) pool_mode_str = cf_get_lookup(&cv); pktbuf_write_DataRow(buf, "ssissiisii", db->name, db->host, db->port, db->dbname, f_user, db->pool_size, db->res_pool_size, pool_mode_str, database_max_connections(db), db->connection_count); } admin_flush(admin, buf, "SHOW"); return true; } /* Command: SHOW LISTS */ static bool admin_show_lists(PgSocket *admin, const char *arg) { PktBuf *buf = pktbuf_dynamic(256); if (!buf) { admin_error(admin, "no mem"); return true; } pktbuf_write_RowDescription(buf, "si", "list", "items"); #define SENDLIST(name, size) pktbuf_write_DataRow(buf, "si", (name), (size)) SENDLIST("databases", statlist_count(&database_list)); SENDLIST("users", statlist_count(&user_list)); SENDLIST("pools", statlist_count(&pool_list)); SENDLIST("free_clients", slab_free_count(client_cache)); SENDLIST("used_clients", slab_active_count(client_cache)); SENDLIST("login_clients", statlist_count(&login_client_list)); SENDLIST("free_servers", slab_free_count(server_cache)); SENDLIST("used_servers", slab_active_count(server_cache)); { int names, zones, qry, pend; adns_info(adns, &names, &zones, &qry, &pend); SENDLIST("dns_names", names); SENDLIST("dns_zones", zones); SENDLIST("dns_queries", qry); SENDLIST("dns_pending", pend); } admin_flush(admin, buf, "SHOW"); return true; } /* Command: SHOW USERS */ static bool admin_show_users(PgSocket *admin, const char *arg) { PgUser *user; struct List *item; PktBuf *buf = pktbuf_dynamic(256); struct CfValue cv; const char *pool_mode_str; if (!buf) { admin_error(admin, "no mem"); return true; } cv.extra = pool_mode_map; pktbuf_write_RowDescription(buf, "ss", "name", "pool_mode"); statlist_for_each(item, &user_list) { user = container_of(item, PgUser, head); pool_mode_str = NULL; cv.value_p = &user->pool_mode; if (user->pool_mode != POOL_INHERIT) pool_mode_str = cf_get_lookup(&cv); pktbuf_write_DataRow(buf, "ss", user->name, pool_mode_str); } admin_flush(admin, buf, "SHOW"); return true; } #define SKF_STD "sssssisiTTssis" #define SKF_DBG "sssssisiTTssisiiiiiii" static void socket_header(PktBuf *buf, bool debug) { pktbuf_write_RowDescription(buf, debug ? SKF_DBG : SKF_STD, "type", "user", "database", "state", "addr", "port", "local_addr", "local_port", "connect_time", "request_time", "ptr", "link", "remote_pid", "tls", /* debug follows */ "recv_pos", "pkt_pos", "pkt_remain", "send_pos", "send_remain", "pkt_avail", "send_avail"); } static void adr2txt(const PgAddr *adr, char *dst, unsigned dstlen) { pga_ntop(adr, dst, dstlen); } static void socket_row(PktBuf *buf, PgSocket *sk, const char *state, bool debug) { int pkt_avail = 0, send_avail = 0; int remote_pid; char ptrbuf[128], linkbuf[128]; char l_addr[PGADDR_BUF], r_addr[PGADDR_BUF]; IOBuf *io = sk->sbuf.io; char infobuf[96] = ""; if (io) { pkt_avail = iobuf_amount_parse(sk->sbuf.io); send_avail = iobuf_amount_pending(sk->sbuf.io); } adr2txt(&sk->remote_addr, r_addr, sizeof(r_addr)); adr2txt(&sk->local_addr, l_addr, sizeof(l_addr)); snprintf(ptrbuf, sizeof(ptrbuf), "%p", sk); if (sk->link) snprintf(linkbuf, sizeof(linkbuf), "%p", sk->link); else linkbuf[0] = 0; /* get pid over unix socket */ if (pga_is_unix(&sk->remote_addr)) remote_pid = sk->remote_addr.scred.pid; else remote_pid = 0; /* if that failed, get it from cancel key */ if (is_server_socket(sk) && remote_pid == 0) remote_pid = be32dec(sk->cancel_key); if (sk->sbuf.tls) tls_get_connection_info(sk->sbuf.tls, infobuf, sizeof infobuf); pktbuf_write_DataRow(buf, debug ? SKF_DBG : SKF_STD, is_server_socket(sk) ? "S" :"C", sk->auth_user ? sk->auth_user->name : "(nouser)", sk->pool ? sk->pool->db->name : "(nodb)", state, r_addr, pga_port(&sk->remote_addr), l_addr, pga_port(&sk->local_addr), sk->connect_time, sk->request_time, ptrbuf, linkbuf, remote_pid, infobuf, /* debug */ io ? io->recv_pos : 0, io ? io->parse_pos : 0, sk->sbuf.pkt_remain, io ? io->done_pos : 0, 0, pkt_avail, send_avail); } /* Helper for SHOW CLIENTS */ static void show_socket_list(PktBuf *buf, struct StatList *list, const char *state, bool debug) { struct List *item; PgSocket *sk; statlist_for_each(item, list) { sk = container_of(item, PgSocket, head); socket_row(buf, sk, state, debug); } } /* Command: SHOW CLIENTS */ static bool admin_show_clients(PgSocket *admin, const char *arg) { struct List *item; PgPool *pool; PktBuf *buf = pktbuf_dynamic(256); if (!buf) { admin_error(admin, "no mem"); return true; } socket_header(buf, false); statlist_for_each(item, &pool_list) { pool = container_of(item, PgPool, head); show_socket_list(buf, &pool->active_client_list, "active", false); show_socket_list(buf, &pool->waiting_client_list, "waiting", false); } admin_flush(admin, buf, "SHOW"); return true; } /* Command: SHOW SERVERS */ static bool admin_show_servers(PgSocket *admin, const char *arg) { struct List *item; PgPool *pool; PktBuf *buf; buf = pktbuf_dynamic(256); if (!buf) { admin_error(admin, "no mem"); return true; } socket_header(buf, false); statlist_for_each(item, &pool_list) { pool = container_of(item, PgPool, head); show_socket_list(buf, &pool->active_server_list, "active", false); show_socket_list(buf, &pool->idle_server_list, "idle", false); show_socket_list(buf, &pool->used_server_list, "used", false); show_socket_list(buf, &pool->tested_server_list, "tested", false); show_socket_list(buf, &pool->new_server_list, "new", false); } admin_flush(admin, buf, "SHOW"); return true; } /* Command: SHOW SOCKETS */ static bool admin_show_sockets(PgSocket *admin, const char *arg) { struct List *item; PgPool *pool; PktBuf *buf; buf = pktbuf_dynamic(256); if (!buf) { admin_error(admin, "no mem"); return true; } socket_header(buf, true); statlist_for_each(item, &pool_list) { pool = container_of(item, PgPool, head); show_socket_list(buf, &pool->active_client_list, "cl_active", true); show_socket_list(buf, &pool->waiting_client_list, "cl_waiting", true); show_socket_list(buf, &pool->active_server_list, "sv_active", true); show_socket_list(buf, &pool->idle_server_list, "sv_idle", true); show_socket_list(buf, &pool->used_server_list, "sv_used", true); show_socket_list(buf, &pool->tested_server_list, "sv_tested", true); show_socket_list(buf, &pool->new_server_list, "sv_login", true); } show_socket_list(buf, &login_client_list, "cl_login", true); admin_flush(admin, buf, "SHOW"); return true; } static void show_active_socket_list(PktBuf *buf, struct StatList *list, const char *state) { struct List *item; statlist_for_each(item, list) { PgSocket *sk = container_of(item, PgSocket, head); if (!sbuf_is_empty(&sk->sbuf)) socket_row(buf, sk, state, true); } } /* Command: SHOW ACTIVE_SOCKETS */ static bool admin_show_active_sockets(PgSocket *admin, const char *arg) { struct List *item; PgPool *pool; PktBuf *buf; buf = pktbuf_dynamic(256); if (!buf) { admin_error(admin, "no mem"); return true; } socket_header(buf, true); statlist_for_each(item, &pool_list) { pool = container_of(item, PgPool, head); show_active_socket_list(buf, &pool->active_client_list, "cl_active"); show_active_socket_list(buf, &pool->waiting_client_list, "cl_waiting"); show_active_socket_list(buf, &pool->active_server_list, "sv_active"); show_active_socket_list(buf, &pool->idle_server_list, "sv_idle"); show_active_socket_list(buf, &pool->used_server_list, "sv_used"); show_active_socket_list(buf, &pool->tested_server_list, "sv_tested"); show_active_socket_list(buf, &pool->new_server_list, "sv_login"); } show_active_socket_list(buf, &login_client_list, "cl_login"); admin_flush(admin, buf, "SHOW"); return true; } /* Command: SHOW POOLS */ static bool admin_show_pools(PgSocket *admin, const char *arg) { struct List *item; PgPool *pool; PktBuf *buf; PgSocket *waiter; usec_t now = get_cached_time(); struct CfValue cv; int pool_mode; cv.extra = pool_mode_map; cv.value_p = &pool_mode; buf = pktbuf_dynamic(256); if (!buf) { admin_error(admin, "no mem"); return true; } pktbuf_write_RowDescription(buf, "ssiiiiiiiis", "database", "user", "cl_active", "cl_waiting", "sv_active", "sv_idle", "sv_used", "sv_tested", "sv_login", "maxwait", "pool_mode"); statlist_for_each(item, &pool_list) { pool = container_of(item, PgPool, head); waiter = first_socket(&pool->waiting_client_list); pool_mode = pool_pool_mode(pool); pktbuf_write_DataRow(buf, "ssiiiiiiiis", pool->db->name, pool->user->name, statlist_count(&pool->active_client_list), statlist_count(&pool->waiting_client_list), statlist_count(&pool->active_server_list), statlist_count(&pool->idle_server_list), statlist_count(&pool->used_server_list), statlist_count(&pool->tested_server_list), statlist_count(&pool->new_server_list), /* how long is the oldest client waited */ (waiter && waiter->query_start) ? (int)((now - waiter->query_start) / USEC) : 0, cf_get_lookup(&cv)); } admin_flush(admin, buf, "SHOW"); return true; } static void slab_stat_cb(void *arg, const char *slab_name, unsigned size, unsigned free, unsigned total) { PktBuf *buf = arg; unsigned alloc = total * size; pktbuf_write_DataRow(buf, "siiii", slab_name, size, total - free, free, alloc); } /* Command: SHOW MEM */ static bool admin_show_mem(PgSocket *admin, const char *arg) { PktBuf *buf; buf = pktbuf_dynamic(256); if (!buf) { admin_error(admin, "no mem"); return true; } pktbuf_write_RowDescription(buf, "siiii", "name", "size", "used", "free", "memtotal"); slab_stats(slab_stat_cb, buf); admin_flush(admin, buf, "SHOW"); return true; } /* Command: SHOW DNS_HOSTS */ static void dns_name_cb(void *arg, const char *name, const struct addrinfo *ai, usec_t ttl) { PktBuf *buf = arg; char *s, *end; char adrs[1024]; usec_t now = get_cached_time(); end = adrs + sizeof(adrs) - 2; for (s = adrs; ai && s < end; ai = ai->ai_next) { if (s != adrs) *s++ = ','; sa2str(ai->ai_addr, s, end - s); s += strlen(s); } *s = 0; pktbuf_write_DataRow(buf, "sqs", name, (ttl - now) / USEC, adrs); } static bool admin_show_dns_hosts(PgSocket *admin, const char *arg) { PktBuf *buf; buf = pktbuf_dynamic(256); if (!buf) { admin_error(admin, "no mem"); return true; } pktbuf_write_RowDescription(buf, "sqs", "hostname", "ttl", "addrs"); adns_walk_names(adns, dns_name_cb, buf); admin_flush(admin, buf, "SHOW"); return true; } /* Command: SHOW DNS_ZONES */ static void dns_zone_cb(void *arg, const char *name, uint32_t serial, int nhosts) { PktBuf *buf = arg; pktbuf_write_DataRow(buf, "sqi", name, (uint64_t)serial, nhosts); } static bool admin_show_dns_zones(PgSocket *admin, const char *arg) { PktBuf *buf; buf = pktbuf_dynamic(256); if (!buf) { admin_error(admin, "no mem"); return true; } pktbuf_write_RowDescription(buf, "sqi", "zonename", "serial", "count"); adns_walk_zones(adns, dns_zone_cb, buf); admin_flush(admin, buf, "SHOW"); return true; } /* Command: SHOW CONFIG */ static void show_one_param(void *arg, const char *name, const char *val, bool reloadable) { PktBuf *buf = arg; pktbuf_write_DataRow(buf, "sss", name, val, reloadable ? "yes" : "no"); } static bool admin_show_config(PgSocket *admin, const char *arg) { PktBuf *buf; buf = pktbuf_dynamic(256); if (!buf) { admin_error(admin, "no mem"); return true; } pktbuf_write_RowDescription(buf, "sss", "key", "value", "changeable"); config_for_each(show_one_param, buf); admin_flush(admin, buf, "SHOW"); return true; } /* Command: RELOAD */ static bool admin_cmd_reload(PgSocket *admin, const char *arg) { if (arg && *arg) return syntax_error(admin); if (!admin->admin_user) return admin_error(admin, "admin access needed"); log_info("RELOAD command issued"); load_config(); return admin_ready(admin, "RELOAD"); } /* Command: SHUTDOWN */ static bool admin_cmd_shutdown(PgSocket *admin, const char *arg) { if (arg && *arg) return syntax_error(admin); if (!admin->admin_user) return admin_error(admin, "admin access needed"); /* * note: new pooler expects unix socket file gone when it gets * event from fd. Currently atexit() cleanup should be called * before closing open sockets. */ log_info("SHUTDOWN command issued"); cf_shutdown = 2; event_loopbreak(); return true; } static void full_resume(void) { int tmp_mode = cf_pause_mode; cf_pause_mode = P_NONE; if (tmp_mode == P_SUSPEND) resume_all(); /* avoid surprise later if cf_shutdown stays set */ if (cf_shutdown) { log_info("canceling shutdown"); cf_shutdown = 0; } } /* Command: RESUME */ static bool admin_cmd_resume(PgSocket *admin, const char *arg) { if (!admin->admin_user) return admin_error(admin, "admin access needed"); if (!arg[0]) { log_info("RESUME command issued"); if (cf_pause_mode != P_NONE) full_resume(); else return admin_error(admin, "Pooler is not paused/suspended"); } else { PgDatabase *db = find_database(arg); log_info("RESUME '%s' command issued", arg); if (db == NULL) return admin_error(admin, "no such database: %s", arg); if (!db->db_paused) return admin_error(admin, "database %s is not paused", arg); db->db_paused = 0; } return admin_ready(admin, "RESUME"); } /* Command: SUSPEND */ static bool admin_cmd_suspend(PgSocket *admin, const char *arg) { if (arg && *arg) return syntax_error(admin); if (!admin->admin_user) return admin_error(admin, "admin access needed"); if (cf_pause_mode) return admin_error(admin, "already suspended/paused"); /* suspend needs to be able to flush buffers */ if (count_paused_databases() > 0) return admin_error(admin, "cannot suspend with paused databases"); log_info("SUSPEND command issued"); cf_pause_mode = P_SUSPEND; admin->wait_for_response = 1; suspend_pooler(); g_suspend_start = get_cached_time(); return true; } /* Command: PAUSE */ static bool admin_cmd_pause(PgSocket *admin, const char *arg) { if (!admin->admin_user) return admin_error(admin, "admin access needed"); if (cf_pause_mode) return admin_error(admin, "already suspended/paused"); if (!arg[0]) { log_info("PAUSE command issued"); cf_pause_mode = P_PAUSE; admin->wait_for_response = 1; } else { PgDatabase *db; log_info("PAUSE '%s' command issued", arg); db = find_or_register_database(admin, arg); if (db == NULL) return admin_error(admin, "no such database: %s", arg); if (db == admin->pool->db) return admin_error(admin, "cannot pause admin db: %s", arg); db->db_paused = 1; if (count_db_active(db) > 0) admin->wait_for_response = 1; else return admin_ready(admin, "PAUSE"); } return true; } /* Command: DISABLE */ static bool admin_cmd_disable(PgSocket *admin, const char *arg) { PgDatabase *db; if (!admin->admin_user) return admin_error(admin, "admin access needed"); if (!arg[0]) return admin_error(admin, "a database is required"); log_info("DISABLE '%s' command issued", arg); db = find_or_register_database(admin, arg); if (db == NULL) return admin_error(admin, "no such database: %s", arg); if (db->admin) return admin_error(admin, "cannot disable admin db: %s", arg); db->db_disabled = 1; return admin_ready(admin, "DISABLE"); } /* Command: ENABLE */ static bool admin_cmd_enable(PgSocket *admin, const char *arg) { PgDatabase *db; if (!admin->admin_user) return admin_error(admin, "admin access needed"); if (!arg[0]) return admin_error(admin, "a database is required"); log_info("ENABLE '%s' command issued", arg); db = find_database(arg); if (db == NULL) return admin_error(admin, "no such database: %s", arg); if (db->admin) return admin_error(admin, "cannot disable admin db: %s", arg); db->db_disabled = 0; return admin_ready(admin, "ENABLE"); } /* Command: KILL */ static bool admin_cmd_kill(PgSocket *admin, const char *arg) { struct List *item, *tmp; PgDatabase *db; PgPool *pool; if (!admin->admin_user) return admin_error(admin, "admin access needed"); if (cf_pause_mode) return admin_error(admin, "already suspended/paused"); if (!arg[0]) return admin_error(admin, "a database is required"); log_info("KILL '%s' command issued", arg); db = find_or_register_database(admin, arg); if (db == NULL) return admin_error(admin, "no such database: %s", arg); if (db == admin->pool->db) return admin_error(admin, "cannot kill admin db: %s", arg); db->db_paused = 1; statlist_for_each_safe(item, &pool_list, tmp) { pool = container_of(item, PgPool, head); if (pool->db == db) kill_pool(pool); } return admin_ready(admin, "KILL"); } /* extract substring from regex group */ static bool copy_arg(const char *src, regmatch_t *glist, int gnum, char *dst, unsigned dstmax, char qchar) { regmatch_t *g = &glist[gnum]; unsigned len; const char *s; char *d = dst; unsigned i; /* no match, if regex allows, it must be fine */ if (g->rm_so < 0 || g->rm_eo < 0) { dst[0] = 0; return true; } len = g->rm_eo - g->rm_so; s = src + g->rm_so; /* too big value */ if (len >= dstmax) { dst[0] = 0; return false; } /* copy and unquote */ if (*s == qchar) { for (i = 1; i < len - 1; i++) { if (s[i] == qchar && s[i+1] == qchar) i++; *d++ = s[i]; } len = d - dst; } else { memcpy(dst, s, len); } dst[len] = 0; return true; } static bool admin_show_help(PgSocket *admin, const char *arg) { bool res; SEND_generic(res, admin, 'N', "sssss", "SNOTICE", "C00000", "MConsole usage", "D\n\tSHOW HELP|CONFIG|DATABASES" "|POOLS|CLIENTS|SERVERS|VERSION\n" "\tSHOW STATS|FDS|SOCKETS|ACTIVE_SOCKETS|LISTS|MEM\n" "\tSHOW DNS_HOSTS|DNS_ZONES\n" "\tSET key = arg\n" "\tRELOAD\n" "\tPAUSE []\n" "\tRESUME []\n" "\tDISABLE \n" "\tENABLE \n" "\tKILL \n" "\tSUSPEND\n" "\tSHUTDOWN", ""); if (res) res = admin_ready(admin, "SHOW"); return res; } static bool admin_show_version(PgSocket *admin, const char *arg) { bool res; SEND_generic(res, admin, 'N', "ssss", "SNOTICE", "C00000", "M" FULLVER, ""); if (res) res = admin_ready(admin, "SHOW"); return res; } static bool admin_show_stats(PgSocket *admin, const char *arg) { return admin_database_stats(admin, &pool_list); } static bool admin_show_totals(PgSocket *admin, const char *arg) { return show_stat_totals(admin, &pool_list); } static struct cmd_lookup show_map [] = { {"clients", admin_show_clients}, {"config", admin_show_config}, {"databases", admin_show_databases}, {"fds", admin_show_fds}, {"help", admin_show_help}, {"lists", admin_show_lists}, {"pools", admin_show_pools}, {"servers", admin_show_servers}, {"sockets", admin_show_sockets}, {"active_sockets", admin_show_active_sockets}, {"stats", admin_show_stats}, {"users", admin_show_users}, {"version", admin_show_version}, {"totals", admin_show_totals}, {"mem", admin_show_mem}, {"dns_hosts", admin_show_dns_hosts}, {"dns_zones", admin_show_dns_zones}, {NULL, NULL} }; static bool admin_cmd_show(PgSocket *admin, const char *arg) { if (fake_show(admin, arg)) return true; return exec_cmd(show_map, admin, arg, NULL); } static struct cmd_lookup cmd_list [] = { {"disable", admin_cmd_disable}, {"enable", admin_cmd_enable}, {"kill", admin_cmd_kill}, {"pause", admin_cmd_pause}, {"reload", admin_cmd_reload}, {"resume", admin_cmd_resume}, {"select", admin_cmd_show}, {"show", admin_cmd_show}, {"shutdown", admin_cmd_shutdown}, {"suspend", admin_cmd_suspend}, {NULL, NULL} }; /* handle user query */ static bool admin_parse_query(PgSocket *admin, const char *q) { regmatch_t grp[MAX_GROUPS]; char cmd[16]; char arg[64]; char val[256]; bool res; bool ok; current_query = q; if (regexec(&rc_cmd, q, MAX_GROUPS, grp, 0) == 0) { ok = copy_arg(q, grp, CMD_NAME, cmd, sizeof(cmd), '"'); if (!ok) goto failed; ok = copy_arg(q, grp, CMD_ARG, arg, sizeof(arg), '"'); if (!ok) goto failed; res = exec_cmd(cmd_list, admin, cmd, arg); } else if (regexec(&rc_set_str, q, MAX_GROUPS, grp, 0) == 0) { ok = copy_arg(q, grp, SET_KEY, arg, sizeof(arg), '"'); if (!ok || !arg[0]) goto failed; ok = copy_arg(q, grp, SET_VAL, val, sizeof(val), '\''); if (!ok) goto failed; res = admin_set(admin, arg, val); } else if (regexec(&rc_set_word, q, MAX_GROUPS, grp, 0) == 0) { ok = copy_arg(q, grp, SET_KEY, arg, sizeof(arg), '"'); if (!ok || !arg[0]) goto failed; ok = copy_arg(q, grp, SET_VAL, val, sizeof(val), '"'); if (!ok) goto failed; res = admin_set(admin, arg, val); } else res = syntax_error(admin); done: current_query = NULL; if (!res) disconnect_client(admin, true, "failure"); return res; failed: res = admin_error(admin, "bad arguments"); goto done; } /* handle packets */ bool admin_handle_client(PgSocket *admin, PktHdr *pkt) { const char *q; bool res; /* don't tolerate partial packets */ if (incomplete_pkt(pkt)) { disconnect_client(admin, true, "incomplete pkt"); return false; } switch (pkt->type) { case 'Q': if (!mbuf_get_string(&pkt->data, &q)) { disconnect_client(admin, true, "incomplete query"); return false; } log_debug("got admin query: %s", q); res = admin_parse_query(admin, q); if (res) sbuf_prepare_skip(&admin->sbuf, pkt->len); return res; case 'X': disconnect_client(admin, false, "close req"); break; default: admin_error(admin, "unsupported pkt type: %d", pkt_desc(pkt)); disconnect_client(admin, true, "bad pkt"); break; } return false; } /** * Client is unauthenticated, look if it wants to connect * to special "pgbouncer" user. */ bool admin_pre_login(PgSocket *client, const char *username) { uid_t peer_uid = -1; gid_t peer_gid = -1; int res; client->admin_user = 0; client->own_user = 0; /* tag same uid as special */ if (pga_is_unix(&client->remote_addr)) { res = getpeereid(sbuf_socket(&client->sbuf), &peer_uid, &peer_gid); if (res >= 0 && peer_uid == getuid() && strcmp("pgbouncer", username) == 0) { client->auth_user = admin_pool->db->forced_user; client->own_user = 1; client->admin_user = 1; slog_info(client, "pgbouncer access from unix socket"); return true; } } /* * auth_mode=any does not keep original username around, * so username based check has to take place here */ if (cf_auth_type == AUTH_ANY) { if (strlist_contains(cf_admin_users, username)) { client->auth_user = admin_pool->db->forced_user; client->admin_user = 1; return true; } else if (strlist_contains(cf_stats_users, username)) { client->auth_user = admin_pool->db->forced_user; return true; } } return false; } bool admin_post_login(PgSocket *client) { const char *username = client->auth_user->name; if (cf_auth_type == AUTH_ANY) return true; if (client->admin_user || strlist_contains(cf_admin_users, username)) { client->admin_user = 1; return true; } else if (strlist_contains(cf_stats_users, username)) { return true; } disconnect_client(client, true, "not allowed"); return false; } /* init special database and query parsing */ void admin_setup(void) { PgDatabase *db; PgPool *pool; PgUser *user; PktBuf *msg; int res; /* fake database */ db = add_database("pgbouncer"); if (!db) fatal("no memory for admin database"); db->port = cf_listen_port; db->pool_size = 2; db->admin = 1; db->pool_mode = POOL_STMT; if (!force_user(db, "pgbouncer", "")) fatal("no mem on startup - cannot alloc pgbouncer user"); /* fake pool */ pool = get_pool(db, db->forced_user); if (!pool) fatal("cannot create admin pool?"); admin_pool = pool; /* user */ user = find_user("pgbouncer"); if (!user) { /* fake user with disabled psw */ user = add_user("pgbouncer", ""); if (!user) fatal("cannot create admin user?"); } /* prepare welcome */ msg = pktbuf_dynamic(128); if (!msg) fatal("cannot create admin welcome"); pktbuf_write_AuthenticationOk(msg); pktbuf_write_ParameterStatus(msg, "server_version", PACKAGE_VERSION "/bouncer"); pktbuf_write_ParameterStatus(msg, "client_encoding", "UNICODE"); pktbuf_write_ParameterStatus(msg, "server_encoding", "SQL_ASCII"); pktbuf_write_ParameterStatus(msg, "DateStyle", "ISO"); pktbuf_write_ParameterStatus(msg, "TimeZone", "GMT"); pktbuf_write_ParameterStatus(msg, "standard_conforming_strings", "on"); pktbuf_write_ParameterStatus(msg, "is_superuser", "on"); if (msg->failed) fatal("admin welcome failed"); pool->welcome_msg = msg; pool->welcome_msg_ready = 1; 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); if (res != 0) fatal("cmd regex compilation error"); res = regcomp(&rc_set_word, cmd_set_word_rx, REG_EXTENDED | REG_ICASE); if (res != 0) fatal("set/word regex compilation error"); res = regcomp(&rc_set_str, cmd_set_str_rx, REG_EXTENDED | REG_ICASE); if (res != 0) fatal("set/str regex compilation error"); } void admin_pause_done(void) { struct List *item, *tmp; PgSocket *admin; bool res; statlist_for_each_safe(item, &admin_pool->active_client_list, tmp) { admin = container_of(item, PgSocket, head); if (!admin->wait_for_response) continue; res = false; switch (cf_pause_mode) { case P_PAUSE: res = admin_ready(admin, "PAUSE"); break; case P_SUSPEND: res = admin_ready(admin, "SUSPEND"); break; default: if (count_paused_databases() > 0) res = admin_ready(admin, "PAUSE"); else /* FIXME */ fatal("admin_pause_done: bad state"); } if (!res) disconnect_client(admin, false, "dead admin"); else admin->wait_for_response = 0; } if (statlist_empty(&admin_pool->active_client_list) && cf_pause_mode == P_SUSPEND) { log_info("Admin disappeared when suspended, doing RESUME"); cf_pause_mode = P_NONE; resume_all(); } } /* admin on console has pressed ^C */ void admin_handle_cancel(PgSocket *admin) { bool res; /* weird, but no reason to fail */ if (!admin->wait_for_response) slog_warning(admin, "admin cancel request for non-waiting client?"); if (cf_pause_mode != P_NONE) full_resume(); /* notify readiness */ SEND_ReadyForQuery(res, admin); if (!res) disconnect_client(admin, false, "readiness send failed"); } pgbouncer-1.7/src/server.c0000664000175000017500000003475512630244203012543 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Handling of server connections */ #include "bouncer.h" static bool load_parameter(PgSocket *server, PktHdr *pkt, bool startup) { const char *key, *val; PgSocket *client = server->link; /* * Want to see complete packet. That means SMALL_PKT * in sbuf.c must be larger than max param pkt. */ if (incomplete_pkt(pkt)) return false; if (!mbuf_get_string(&pkt->data, &key)) goto failed; if (!mbuf_get_string(&pkt->data, &val)) goto failed; slog_debug(server, "S: param: %s = %s", key, val); varcache_set(&server->vars, key, val); if (client) { slog_debug(client, "setting client var: %s='%s'", key, val); varcache_set(&client->vars, 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 */ static void kill_pool_logins(PgPool *pool, PktHdr *errpkt) { struct List *item, *tmp; PgSocket *client; const char *level, *msg; parse_server_error(errpkt, &level, &msg); log_warning("server login failed: %s %s", level, msg); statlist_for_each_safe(item, &pool->waiting_client_list, tmp) { client = container_of(item, PgSocket, head); if (!client->wait_for_welcome) continue; disconnect_client(client, true, "%s", msg); } } /* process packets on server auth phase */ static bool handle_server_startup(PgSocket *server, PktHdr *pkt) { SBuf *sbuf = &server->sbuf; bool res = false; const uint8_t *ckey; if (incomplete_pkt(pkt)) { disconnect_server(server, true, "partial pkt in login phase"); return false; } /* ignore most that happens during connect_query */ if (server->exec_on_connect) { switch (pkt->type) { case 'Z': case 'S': /* handle them below */ break; case 'E': /* log & ignore errors */ log_server_error("S: error while executing exec_on_query", pkt); default: /* ignore rest */ sbuf_prepare_skip(sbuf, pkt->len); return true; } } switch (pkt->type) { default: slog_error(server, "unknown pkt from server: '%c'", pkt_desc(pkt)); disconnect_server(server, true, "unknown pkt from server"); break; case 'E': /* ErrorResponse */ if (!server->pool->welcome_msg_ready) kill_pool_logins(server->pool, pkt); else log_server_error("S: login failed", pkt); disconnect_server(server, true, "login failed"); break; /* packets that need closer look */ case 'R': /* AuthenticationXXX */ slog_debug(server, "calling login_answer"); res = answer_authreq(server, pkt); if (!res) disconnect_server(server, false, "failed to answer authreq"); break; case 'S': /* ParameterStatus */ res = load_parameter(server, pkt, true); break; case 'Z': /* ReadyForQuery */ if (server->exec_on_connect) { server->exec_on_connect = 0; /* deliberately ignore transaction status */ } else if (server->pool->db->connect_query) { server->exec_on_connect = 1; slog_debug(server, "server connect ok, send exec_on_connect"); SEND_generic(res, server, 'Q', "s", server->pool->db->connect_query); if (!res) disconnect_server(server, false, "exec_on_connect query failed"); break; } /* login ok */ slog_debug(server, "server login ok, start accepting queries"); server->ready = 1; /* got all params */ finish_welcome_msg(server); /* need to notify sbuf if server was closed */ res = release_server(server); /* let the takeover process handle it */ if (res && server->pool->db->admin) res = takeover_login(server); break; /* ignorable packets */ case 'K': /* BackendKeyData */ if (!mbuf_get_bytes(&pkt->data, BACKENDKEY_LEN, &ckey)) { disconnect_server(server, true, "bad cancel key"); return false; } memcpy(server->cancel_key, ckey, BACKENDKEY_LEN); res = true; break; case 'N': /* NoticeResponse */ slog_noise(server, "skipping pkt: %c", pkt_desc(pkt)); res = true; break; } if (res) sbuf_prepare_skip(sbuf, pkt->len); return res; } int pool_pool_mode(PgPool *pool) { int pool_mode = pool->user->pool_mode; if (pool_mode == POOL_INHERIT) pool_mode = pool->db->pool_mode; if (pool_mode == POOL_INHERIT) pool_mode = cf_pool_mode; return pool_mode; } int database_max_connections(PgDatabase *db) { if (db->max_db_connections <= 0) { return cf_max_db_connections; } else { return db->max_db_connections; } } int user_max_connections(PgUser *user) { if (user->max_user_connections <= 0) { return cf_max_user_connections; } else { return user->max_user_connections; } } /* process packets on logged in connection */ static bool handle_server_work(PgSocket *server, PktHdr *pkt) { bool ready = false; bool idle_tx = false; char state; SBuf *sbuf = &server->sbuf; PgSocket *client = server->link; Assert(!server->pool->db->admin); switch (pkt->type) { default: slog_error(server, "unknown pkt: '%c'", pkt_desc(pkt)); disconnect_server(server, true, "unknown pkt"); return false; /* pooling decisions will be based on this packet */ case 'Z': /* ReadyForQuery */ /* if partial pkt, wait */ if (!mbuf_get_char(&pkt->data, &state)) return false; /* set ready only if no tx */ if (state == 'I') ready = true; else if (pool_pool_mode(server->pool) == POOL_STMT) { disconnect_server(server, true, "Long transactions not allowed"); return false; } else if (state == 'T' || state == 'E') { idle_tx = true; } if (client && !server->setting_vars) { if (client->expect_rfq_count > 0) { client->expect_rfq_count--; } else if (server->state == SV_ACTIVE) { slog_debug(client, "unexpected ReadyForQuery - expect_rfq_count=%d", client->expect_rfq_count); } } break; case 'S': /* ParameterStatus */ if (!load_parameter(server, pkt, false)) return false; break; /* * 'E' and 'N' packets currently set ->ready to 0. Correct would * be to leave ->ready as-is, because overall TX state stays same. * It matters for connections in IDLE or USED state which get dirty * suddenly but should not as they are still usable. * * But the 'E' or 'N' packet between transactions signifies probably * dying backend. It is better to tag server as dirty and drop * it later. */ case 'E': /* ErrorResponse */ if (server->setting_vars) { /* * the SET and user query will be different TX * so we cannot report SET error to user. */ log_server_error("varcache_apply failed", pkt); /* * client probably gave invalid values in startup pkt. * * no reason to keep such guys. */ disconnect_server(server, true, "invalid server parameter"); return false; } case 'C': /* CommandComplete */ /* ErrorResponse and CommandComplete show end of copy mode */ if (server->copy_mode) { server->copy_mode = false; /* it's impossible to track sync count over copy */ if (client) client->expect_rfq_count = 0; } break; case 'N': /* NoticeResponse */ break; /* reply to LISTEN, don't change connection state */ case 'A': /* NotificationResponse */ idle_tx = server->idle_tx; ready = server->ready; break; /* copy mode */ case 'G': /* CopyInResponse */ case 'H': /* CopyOutResponse */ server->copy_mode = true; break; /* chat packets */ case '2': /* BindComplete */ case '3': /* CloseComplete */ case 'c': /* CopyDone(F/B) */ case 'f': /* CopyFail(F/B) */ case 'I': /* EmptyQueryResponse == CommandComplete */ case 'V': /* FunctionCallResponse */ case 'n': /* NoData */ case '1': /* ParseComplete */ case 's': /* PortalSuspended */ /* data packets, there will be more coming */ case 'd': /* CopyData(F/B) */ case 'D': /* DataRow */ case 't': /* ParameterDescription */ case 'T': /* RowDescription */ break; } server->idle_tx = idle_tx; server->ready = ready; server->pool->stats.server_bytes += pkt->len; if (server->setting_vars) { Assert(client); sbuf_prepare_skip(sbuf, pkt->len); } else if (client) { if (client->state == CL_LOGIN) { return handle_auth_response(client, pkt); } else { sbuf_prepare_send(sbuf, &client->sbuf, pkt->len); if (ready && client->query_start) { usec_t total; total = get_cached_time() - client->query_start; client->query_start = 0; server->pool->stats.query_time += total; slog_debug(client, "query time: %d us", (int)total); } else if (ready) { slog_warning(client, "FIXME: query end, but query_start == 0"); } } } else { if (server->state != SV_TESTED) slog_warning(server, "got packet '%c' from server when not linked", pkt_desc(pkt)); sbuf_prepare_skip(sbuf, pkt->len); } return true; } /* got connection, decide what to do */ static bool handle_connect(PgSocket *server) { bool res = false; PgPool *pool = server->pool; char buf[PGADDR_BUF + 32]; fill_local_addr(server, sbuf_socket(&server->sbuf), pga_is_unix(&server->remote_addr)); if (cf_log_connections) { if (pga_is_unix(&server->remote_addr)) slog_info(server, "new connection to server"); else slog_info(server, "new connection to server (from %s)", pga_str(&server->local_addr, buf, sizeof(buf))); } if (!statlist_empty(&pool->cancel_req_list)) { slog_debug(server, "use it for pending cancel req"); /* if pending cancel req, send it */ forward_cancel_request(server); /* notify disconnect_server() that connect did not fail */ server->ready = 1; disconnect_server(server, false, "sent cancel req"); } else { /* proceed with login */ if (cf_server_tls_sslmode > SSLMODE_DISABLED) { slog_noise(server, "P: SSL request"); res = send_sslreq_packet(server); if (res) server->wait_sslchar = true; } else { slog_noise(server, "P: startup"); res = send_startup_packet(server); } if (!res) disconnect_server(server, false, "startup pkt failed"); } return res; } static bool handle_sslchar(PgSocket *server, struct MBuf *data) { uint8_t schar = '?'; bool ok; server->wait_sslchar = false; ok = mbuf_get_byte(data, &schar); if (!ok || (schar != 'S' && schar != 'N') || mbuf_avail_for_read(data) != 0) { disconnect_server(server, false, "bad sslreq answer"); return false; } if (schar == 'S') { slog_noise(server, "launching tls"); ok = sbuf_tls_connect(&server->sbuf, server->pool->db->host); } else if (cf_server_tls_sslmode >= SSLMODE_REQUIRE) { disconnect_server(server, false, "server refused SSL"); return false; } else { /* proceed with non-TLS connection */ ok = send_startup_packet(server); } if (ok) { sbuf_prepare_skip(&server->sbuf, 1); } else { disconnect_server(server, false, "sslreq processing failed"); } return ok; } /* callback from SBuf */ bool server_proto(SBuf *sbuf, SBufEvent evtype, struct MBuf *data) { bool res = false; PgSocket *server = container_of(sbuf, PgSocket, sbuf); PgPool *pool = server->pool; PktHdr pkt; char infobuf[96]; Assert(is_server_socket(server)); Assert(server->state != SV_FREE); /* may happen if close failed */ if (server->state == SV_JUSTFREE) return false; switch (evtype) { case SBUF_EV_RECV_FAILED: disconnect_server(server, false, "server conn crashed?"); break; case SBUF_EV_SEND_FAILED: disconnect_client(server->link, false, "unexpected eof"); break; case SBUF_EV_READ: if (server->wait_sslchar) { res = handle_sslchar(server, data); break; } if (incomplete_header(data)) { slog_noise(server, "S: got partial header, trying to wait a bit"); break; } /* parse pkt header */ if (!get_header(data, &pkt)) { disconnect_server(server, true, "bad pkt header"); break; } slog_noise(server, "S: pkt '%c', len=%d", pkt_desc(&pkt), pkt.len); server->request_time = get_cached_time(); switch (server->state) { case SV_LOGIN: res = handle_server_startup(server, &pkt); break; case SV_TESTED: case SV_USED: case SV_ACTIVE: case SV_IDLE: res = handle_server_work(server, &pkt); break; default: fatal("server_proto: server in bad state: %d", server->state); } break; case SBUF_EV_CONNECT_FAILED: Assert(server->state == SV_LOGIN); disconnect_server(server, false, "connect failed"); break; case SBUF_EV_CONNECT_OK: slog_debug(server, "S: connect ok"); Assert(server->state == SV_LOGIN); server->request_time = get_cached_time(); res = handle_connect(server); break; case SBUF_EV_FLUSH: res = true; if (!server->ready) break; if (server->setting_vars) { PgSocket *client = server->link; Assert(client); server->setting_vars = 0; sbuf_continue(&client->sbuf); break; } if (pool_pool_mode(pool) != POOL_SESSION || server->state == SV_TESTED || server->resetting) { server->resetting = false; switch (server->state) { case SV_ACTIVE: case SV_TESTED: /* keep link if client expects more Syncs */ if (server->link) { if (server->link->expect_rfq_count > 0) break; } /* retval does not matter here */ release_server(server); break; default: slog_warning(server, "EV_FLUSH with state=%d", server->state); case SV_IDLE: break; } } break; case SBUF_EV_PKT_CALLBACK: slog_warning(server, "SBUF_EV_PKT_CALLBACK with state=%d", server->state); break; case SBUF_EV_TLS_READY: Assert(server->state == SV_LOGIN); tls_get_connection_info(server->sbuf.tls, infobuf, sizeof infobuf); if (cf_log_connections) { slog_info(server, "SSL established: %s", infobuf); } else { slog_noise(server, "SSL established: %s", infobuf); } server->request_time = get_cached_time(); res = send_startup_packet(server); if (res) sbuf_continue(&server->sbuf); else disconnect_server(server, false, "TLS startup failed"); break; } if (!res && pool->db->admin) takeover_login_failed(); return res; } pgbouncer-1.7/src/varcache.c0000664000175000017500000001031112565403475013006 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Operations with server config parameters. */ #include "bouncer.h" #include struct var_lookup { const char *name; enum VarCacheIdx idx; }; static const struct var_lookup lookup [] = { {"client_encoding", VClientEncoding }, {"DateStyle", VDateStyle }, {"TimeZone", VTimeZone }, {"standard_conforming_strings", VStdStr }, {"application_name", VAppName }, {NULL}, }; static struct StrPool *vpool; static inline struct PStr *get_value(VarCache *cache, const struct var_lookup *lk) { return cache->var_list[lk->idx]; } bool varcache_set(VarCache *cache, const char *key, const char *value) { const struct var_lookup *lk; struct PStr *pstr = NULL; if (!vpool) { vpool = strpool_create(USUAL_ALLOC); if (!vpool) return false; } for (lk = lookup; lk->name; lk++) { if (strcasecmp(lk->name, key) == 0) goto set_value; } return false; set_value: /* drop old value */ strpool_decref(cache->var_list[lk->idx]); cache->var_list[lk->idx] = NULL; /* NULL value? */ if (!value) return false; /* set new value */ pstr = strpool_get(vpool, value, strlen(value)); if (!pstr) return false; cache->var_list[lk->idx] = pstr; return true; } static int apply_var(PktBuf *pkt, const char *key, const struct PStr *cval, const struct PStr *sval) { char buf[128]; char qbuf[128]; unsigned len; /* if unset, skip */ if (!cval || !sval || !*cval->str) return 0; /* if equal, skip */ if (cval == sval) return 0; /* ignore case difference */ if (strcasecmp(cval->str, sval->str) == 0) return 0; /* the string may have been taken from startup pkt */ if (!pg_quote_literal(qbuf, cval->str, sizeof(qbuf))) return 0; /* add SET statement to packet */ len = snprintf(buf, sizeof(buf), "SET %s=%s;", key, qbuf); if (len < sizeof(buf)) { pktbuf_put_bytes(pkt, buf, len); return 1; } else { log_warning("got too long value, skipping"); return 0; } } bool varcache_apply(PgSocket *server, PgSocket *client, bool *changes_p) { int changes = 0; struct PStr *cval, *sval; const struct var_lookup *lk; int sql_ofs; struct PktBuf *pkt = pktbuf_temp(); pktbuf_start_packet(pkt, 'Q'); /* grab query position inside pkt */ sql_ofs = pktbuf_written(pkt); for (lk = lookup; lk->name; lk++) { sval = get_value(&server->vars, lk); cval = get_value(&client->vars, lk); changes += apply_var(pkt, lk->name, cval, sval); } *changes_p = changes > 0; if (!changes) return true; pktbuf_put_char(pkt, 0); pktbuf_finish_packet(pkt); slog_debug(server, "varcache_apply: %s", pkt->buf + sql_ofs); return pktbuf_send_immediate(pkt, server); } void varcache_fill_unset(VarCache *src, PgSocket *dst) { struct PStr *srcval, *dstval; const struct var_lookup *lk; for (lk = lookup; lk->name; lk++) { srcval = src->var_list[lk->idx]; dstval = dst->vars.var_list[lk->idx]; if (!dstval) { strpool_incref(srcval); dst->vars.var_list[lk->idx] = srcval; } } } void varcache_clean(VarCache *cache) { int i; for (i = 0; i < NumVars; i++) { strpool_decref(cache->var_list[i]); cache->var_list[i] = NULL; } } void varcache_add_params(PktBuf *pkt, VarCache *vars) { struct PStr *val; const struct var_lookup *lk; for (lk = lookup; lk->name; lk++) { val = vars->var_list[lk->idx]; if (val) pktbuf_write_ParameterStatus(pkt, lk->name, val->str); } } pgbouncer-1.7/src/util.c0000664000175000017500000002360212561345127012211 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Random small utility functions */ #include "bouncer.h" #include #include int log_socket_prefix(enum LogLevel lev, void *ctx, char *dst, unsigned int dstlen) { const struct PgSocket *sock = ctx; const char *user, *db, *host; char host6[PGADDR_BUF]; int port; char stype; /* no prefix */ if (!sock) return 0; /* format prefix */ stype = is_server_socket(sock) ? 'S' : 'C'; port = pga_port(&sock->remote_addr); db = sock->pool ? sock->pool->db->name : "(nodb)"; user = sock->auth_user ? sock->auth_user->name : "(nouser)"; if (pga_is_unix(&sock->remote_addr)) { unsigned long pid = sock->remote_addr.scred.pid; if (pid) { snprintf(host6, sizeof(host6), "unix(%lu)", pid); host = host6; } else { host = "unix"; } } else { host = pga_ntop(&sock->remote_addr, host6, sizeof(host6)); } if (pga_family(&sock->remote_addr) == AF_INET6) { return snprintf(dst, dstlen, "%c-%p: %s/%s@[%s]:%d ", stype, sock, db, user, host, port); } else { return snprintf(dst, dstlen, "%c-%p: %s/%s@%s:%d ", stype, sock, db, user, host, port); } } const char *bin2hex(const uint8_t *src, unsigned srclen, char *dst, unsigned dstlen) { unsigned int i, j; static const char hextbl [] = "0123456789abcdef"; if (!dstlen) return ""; if (srclen*2+1 > dstlen) srclen = (dstlen - 1) / 2; for (i = j = 0; i < srclen; i++) { dst[j++] = hextbl[src[i] >> 4]; dst[j++] = hextbl[src[i] & 15]; } dst[j] = 0; return dst; } /* * PostgreSQL MD5 hashing. */ static void hash2hex(const uint8_t *hash, char *dst) { bin2hex(hash, MD5_DIGEST_LENGTH, dst, 16*2+1); } void pg_md5_encrypt(const char *part1, const char *part2, size_t part2len, char *dest) { struct md5_ctx ctx; uint8_t hash[MD5_DIGEST_LENGTH]; md5_reset(&ctx); md5_update(&ctx, part1, strlen(part1)); md5_update(&ctx, part2, part2len); md5_final(&ctx, hash); memcpy(dest, "md5", 3); hash2hex(hash, dest + 3); } /* wrapped for getting random bytes */ void get_random_bytes(uint8_t *dest, int len) { csrandom_bytes(dest, len); } /* set needed socket options */ bool tune_socket(int sock, bool is_unix) { int res; int val; bool ok; /* * Generic stuff + nonblock. */ ok = socket_setup(sock, true); if (!ok) goto fail; /* * Following options are for network sockets */ if (is_unix) return true; /* * TCP Keepalive */ ok = socket_set_keepalive(sock, cf_tcp_keepalive, cf_tcp_keepidle, cf_tcp_keepintvl, cf_tcp_keepcnt); if (!ok) goto fail; /* * set in-kernel socket buffer size */ if (cf_tcp_socket_buffer) { val = cf_tcp_socket_buffer; res = setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &val, sizeof(val)); if (res < 0) goto fail; val = cf_tcp_socket_buffer; res = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val)); if (res < 0) goto fail; } /* * Turn off kernel buffering, each send() will be one packet. */ val = 1; res = setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)); if (res < 0) goto fail; return true; fail: log_warning("tune_socket(%d) failed: %s", sock, strerror(errno)); return false; } /* * Find a string in comma-separated list. * * It does not support space inside tokens. */ bool strlist_contains(const char *liststr, const char *str) { int c, len = strlen(str); const char *p, *listpos = liststr; loop: /* find string fragment, later check if actual token */ p = strstr(listpos, str); if (p == NULL) return false; /* move listpos further */ listpos = p + len; /* survive len=0 and avoid unnecessary compare */ if (*listpos) listpos++; /* check previous symbol */ if (p > liststr) { c = *(p - 1); if (!isspace(c) && c != ',') goto loop; } /* check following symbol */ c = p[len]; if (c != 0 && !isspace(c) && c != ',') goto loop; return true; } void fill_remote_addr(PgSocket *sk, int fd, bool is_unix) { PgAddr *dst = &sk->remote_addr; socklen_t len = sizeof(PgAddr); int err; if (is_unix) { uid_t uid = 0; gid_t gid = 0; pid_t pid = 0; pga_set(dst, AF_UNIX, cf_listen_port); if (getpeercreds(fd, &uid, &gid, &pid) >= 0) { log_noise("unix peer uid: %d", (int)uid); } else { log_warning("unix peer uid failed: %s", strerror(errno)); } dst->scred.uid = uid; dst->scred.pid = pid; } else { err = getpeername(fd, (struct sockaddr *)dst, &len); if (err < 0) { log_error("fill_remote_addr: getpeername(%d) = %s", fd, strerror(errno)); } } } void fill_local_addr(PgSocket *sk, int fd, bool is_unix) { PgAddr *dst = &sk->local_addr; socklen_t len = sizeof(PgAddr); int err; if (is_unix) { pga_set(dst, AF_UNIX, cf_listen_port); dst->scred.uid = geteuid(); dst->scred.pid = getpid(); } else { err = getsockname(fd, (struct sockaddr *)dst, &len); if (err < 0) { log_error("fill_local_addr: getsockname(%d) = %s", fd, strerror(errno)); } } } /* * Error handling around evtimer_add() is nasty as the code * may not be called again. As there is fixed number of timers * in pgbouncer, provider safe_evtimer_add() that stores args of * failed calls in static array and retries later. */ #define TIMER_BACKUP_SLOTS 10 struct timer_slot { struct event *ev; struct timeval tv; }; static struct timer_slot timer_backup_list[TIMER_BACKUP_SLOTS]; static int timer_backup_used = 0; void safe_evtimer_add(struct event *ev, struct timeval *tv) { int res; struct timer_slot *ts; res = evtimer_add(ev, tv); if (res >= 0) return; if (timer_backup_used >= TIMER_BACKUP_SLOTS) fatal_perror("TIMER_BACKUP_SLOTS full"); ts = &timer_backup_list[timer_backup_used++]; ts->ev = ev; ts->tv = *tv; } void rescue_timers(void) { struct timer_slot *ts; while (timer_backup_used) { ts = &timer_backup_list[timer_backup_used - 1]; if (evtimer_add(ts->ev, &ts->tv) < 0) break; timer_backup_used--; } } /* * PgAddr operations */ int pga_port(const PgAddr *a) { if (a->sa.sa_family == AF_INET6) { return ntohs(a->sin6.sin6_port); } else { return ntohs(a->sin.sin_port); } } /* set family and port */ void pga_set(PgAddr *a, int af, int port) { memset(a, 0, sizeof(*a)); if (af == AF_INET6) { a->sin6.sin6_family = af; a->sin6.sin6_port = htons(port); } else { a->sin.sin_family = af; a->sin.sin_port = htons(port); } } /* copy sockaddr_in/in6 to PgAddr */ void pga_copy(PgAddr *a, const struct sockaddr *sa) { switch (sa->sa_family) { case AF_INET: memcpy(&a->sin, sa, sizeof(a->sin)); break; case AF_INET6: memcpy(&a->sin6, sa, sizeof(a->sin6)); break; case AF_UNIX: log_error("pga_copy: AF_UNIX copy not supported"); } } int pga_cmp_addr(const PgAddr *a, const PgAddr *b) { if (pga_family(a) != pga_family(b)) return pga_family(a) - pga_family(b); switch (pga_family(a)) { case AF_INET: return memcmp(&a->sin.sin_addr, &b->sin.sin_addr, sizeof(a->sin.sin_addr)); break; case AF_INET6: return memcmp(&a->sin6.sin6_addr, &b->sin6.sin6_addr, sizeof(a->sin6.sin6_addr)); break; default: log_error("pga_cmp_addr: unsupported family"); return 0; } } /* convert pgaddr to string */ const char *pga_ntop(const PgAddr *a, char *dst, int dstlen) { const char *res = NULL; char buf[PGADDR_BUF]; memset(buf, 0, sizeof(buf)); switch (pga_family(a)) { case AF_UNIX: res = "unix"; break; case AF_INET: res = inet_ntop(AF_INET, &a->sin.sin_addr, buf, sizeof(buf)); break; case AF_INET6: res = inet_ntop(AF_INET6, &a->sin6.sin6_addr, buf, sizeof(buf)); break; default: res = "(bad-af)"; } if (res == NULL) res = "(err-ntop)"; strlcpy(dst, res, dstlen); return dst; } /* parse address from string */ bool pga_pton(PgAddr *a, const char *s, int port) { int res = 1; if (strcmp(s, "unix") == 0) { pga_set(a, AF_UNIX, port); } else if (strcmp(s, "*") == 0) { pga_set(a, AF_INET, port); a->sin.sin_addr.s_addr = htonl(INADDR_ANY); } else if (strchr(s, ':')) { pga_set(a, AF_INET6, port); res = inet_pton(AF_INET6, s, &a->sin6.sin6_addr); } else { pga_set(a, AF_INET, port); res = inet_pton(AF_INET, s, &a->sin.sin_addr); } if (res == 0) errno = EINVAL; return res > 0; } const char *pga_str(const PgAddr *a, char *dst, int dstlen) { char buf[PGADDR_BUF]; pga_ntop(a, buf, sizeof(buf)); if (pga_family(a) == AF_INET6) { snprintf(dst, dstlen, "[%s]:%d", buf, pga_port(a)); } else if (pga_family(a) == AF_UNIX && a->scred.pid) { snprintf(dst, dstlen, "%s:%d$%u", buf, pga_port(a), a->scred.pid); } else { snprintf(dst, dstlen, "%s:%d", buf, pga_port(a)); } return dst; } static const char *cached_hostname(void) { static char cache[256]; int err; if (cache[0] == 0) { err = gethostname(cache, sizeof(cache)); if (err != 0) strlcpy(cache, "somehost", sizeof(cache)); } return cache; } const char *pga_details(const PgAddr *a, char *dst, int dstlen) { char buf[PGADDR_BUF]; pga_ntop(a, buf, sizeof(buf)); if (pga_family(a) == AF_INET6) { snprintf(dst, dstlen, "[%s]:%d", buf, pga_port(a)); } else if (pga_family(a) == AF_UNIX && a->scred.pid) { snprintf(dst, dstlen, "%s(%u@%s):%d", buf, a->scred.pid, cached_hostname(), pga_port(a)); } else { snprintf(dst, dstlen, "%s:%d", buf, pga_port(a)); } return dst; } pgbouncer-1.7/src/janitor.c0000664000175000017500000004274312565403475012716 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Periodic maintenance. */ #include "bouncer.h" /* do full maintenance 3x per second */ static struct timeval full_maint_period = {0, USEC / 3}; static struct event full_maint_ev; /* close all sockets in server list */ static void close_server_list(struct StatList *sk_list, const char *reason) { struct List *item, *tmp; PgSocket *server; statlist_for_each_safe(item, sk_list, tmp) { server = container_of(item, PgSocket, head); disconnect_server(server, true, "%s", reason); } } static void close_client_list(struct StatList *sk_list, const char *reason) { struct List *item, *tmp; PgSocket *client; statlist_for_each_safe(item, sk_list, tmp) { client = container_of(item, PgSocket, head); disconnect_client(client, true, "%s", reason); } } bool suspend_socket(PgSocket *sk, bool force_suspend) { if (sk->suspended) return true; if (sbuf_is_empty(&sk->sbuf)) { if (sbuf_pause(&sk->sbuf)) sk->suspended = 1; } if (sk->suspended || !force_suspend) return sk->suspended; if (is_server_socket(sk)) disconnect_server(sk, true, "suspend_timeout"); else disconnect_client(sk, true, "suspend_timeout"); return true; } /* suspend all sockets in socket list */ static int suspend_socket_list(struct StatList *list, bool force_suspend) { struct List *item, *tmp; PgSocket *sk; int active = 0; statlist_for_each_safe(item, list, tmp) { sk = container_of(item, PgSocket, head); if (!suspend_socket(sk, force_suspend)) active++; } return active; } /* resume all suspended sockets in socket list */ static void resume_socket_list(struct StatList *list) { struct List *item, *tmp; PgSocket *sk; statlist_for_each_safe(item, list, tmp) { sk = container_of(item, PgSocket, head); if (sk->suspended) { sk->suspended = 0; sbuf_continue(&sk->sbuf); } } } /* resume all suspended sockets in all pools */ static void resume_sockets(void) { struct List *item; PgPool *pool; statlist_for_each(item, &pool_list) { pool = container_of(item, PgPool, head); if (pool->db->admin) continue; resume_socket_list(&pool->active_client_list); resume_socket_list(&pool->active_server_list); resume_socket_list(&pool->idle_server_list); resume_socket_list(&pool->used_server_list); } } /* resume pools and listen sockets */ void resume_all(void) { resume_sockets(); resume_pooler(); } /* * send test/reset query to server if needed */ static void launch_recheck(PgPool *pool) { const char *q = cf_server_check_query; bool need_check = true; PgSocket *server; bool res = true; /* find clean server */ while (1) { server = first_socket(&pool->used_server_list); if (!server) return; if (server->ready) break; disconnect_server(server, true, "idle server got dirty"); } /* is the check needed? */ if (q == NULL || q[0] == 0) { need_check = false; } else if (cf_server_check_delay > 0) { usec_t now = get_cached_time(); if (now - server->request_time < cf_server_check_delay) need_check = false; } if (need_check) { /* send test query, wait for result */ slog_debug(server, "P: Checking: %s", q); change_server_state(server, SV_TESTED); SEND_generic(res, server, 'Q', "s", q); if (!res) disconnect_server(server, false, "test query failed"); } else { /* make immediately available */ release_server(server); } } /* * make servers available */ static void per_loop_activate(PgPool *pool) { struct List *item, *tmp; PgSocket *client; int sv_tested, sv_used; /* see if any server have been freed */ sv_tested = statlist_count(&pool->tested_server_list); sv_used = statlist_count(&pool->used_server_list); statlist_for_each_safe(item, &pool->waiting_client_list, tmp) { client = container_of(item, PgSocket, head); if (!statlist_empty(&pool->idle_server_list)) { /* db not fully initialized after reboot */ if (client->wait_for_welcome && !pool->welcome_msg_ready) { launch_new_connection(pool); continue; } /* there is a ready server already */ activate_client(client); } else if (sv_tested > 0) { /* some connections are in testing process */ --sv_tested; } else if (sv_used > 0) { /* ask for more connections to be tested */ launch_recheck(pool); --sv_used; } else { /* not enough connections */ launch_new_connection(pool); break; } } } /* * pause active clients */ static int per_loop_pause(PgPool *pool) { int active = 0; if (pool->db->admin) return 0; close_server_list(&pool->idle_server_list, "pause mode"); close_server_list(&pool->used_server_list, "pause mode"); close_server_list(&pool->new_server_list, "pause mode"); active += statlist_count(&pool->active_server_list); active += statlist_count(&pool->tested_server_list); return active; } /* * suspend active clients and servers */ static int per_loop_suspend(PgPool *pool, bool force_suspend) { int active = 0; if (pool->db->admin) return 0; active += suspend_socket_list(&pool->active_client_list, force_suspend); /* this list is not suspendable, but still need force_suspend and counting */ active += suspend_socket_list(&pool->waiting_client_list, force_suspend); if (active) per_loop_activate(pool); if (!active) { active += suspend_socket_list(&pool->active_server_list, force_suspend); active += suspend_socket_list(&pool->idle_server_list, force_suspend); /* as all clients are done, no need for them */ close_server_list(&pool->tested_server_list, "close unsafe file descriptors on suspend"); close_server_list(&pool->used_server_list, "close unsafe file descriptors on suspend"); } return active; } /* * this function is called for each event loop. */ void per_loop_maint(void) { struct List *item; PgPool *pool; int active = 0; int partial_pause = 0; bool force_suspend = false; if (cf_pause_mode == P_SUSPEND && cf_suspend_timeout > 0) { usec_t stime = get_cached_time() - g_suspend_start; if (stime >= cf_suspend_timeout) force_suspend = true; } statlist_for_each(item, &pool_list) { pool = container_of(item, PgPool, head); if (pool->db->admin) continue; switch (cf_pause_mode) { case P_NONE: if (pool->db->db_paused) { partial_pause = 1; active += per_loop_pause(pool); } else { per_loop_activate(pool); } break; case P_PAUSE: active += per_loop_pause(pool); break; case P_SUSPEND: active += per_loop_suspend(pool, force_suspend); break; } } switch (cf_pause_mode) { case P_SUSPEND: if (force_suspend) { close_client_list(&login_client_list, "suspend_timeout"); } else { active += statlist_count(&login_client_list); } case P_PAUSE: if (!active) admin_pause_done(); break; case P_NONE: if (partial_pause && !active) admin_pause_done(); break; } } /* maintaining clients in pool */ static void pool_client_maint(PgPool *pool) { struct List *item, *tmp; usec_t now = get_cached_time(); PgSocket *client; usec_t age; /* force client_idle_timeout */ if (cf_client_idle_timeout > 0) { statlist_for_each_safe(item, &pool->active_client_list, tmp) { client = container_of(item, PgSocket, head); Assert(client->state == CL_ACTIVE); if (client->link) continue; if (now - client->request_time > cf_client_idle_timeout) disconnect_client(client, true, "client_idle_timeout"); } } /* force timeouts for waiting queries */ if (cf_query_timeout > 0 || cf_query_wait_timeout > 0) { statlist_for_each_safe(item, &pool->waiting_client_list, tmp) { client = container_of(item, PgSocket, head); Assert(client->state == CL_WAITING || client->state == CL_WAITING_LOGIN); if (client->query_start == 0) { age = now - client->request_time; /* log_warning("query_start==0"); */ } else { age = now - client->query_start; } if (cf_query_timeout > 0 && age > cf_query_timeout) { disconnect_client(client, true, "query_timeout"); } else if (cf_query_wait_timeout > 0 && age > cf_query_wait_timeout) { disconnect_client(client, true, "query_wait_timeout"); } } } /* apply client_login_timeout to clients waiting for welcome pkt */ if (cf_client_login_timeout > 0 && !pool->welcome_msg_ready) { statlist_for_each_safe(item, &pool->waiting_client_list, tmp) { client = container_of(item, PgSocket, head); if (!client->wait_for_welcome) continue; age = now - client->connect_time; if (age > cf_client_login_timeout) disconnect_client(client, true, "client_login_timeout (server down)"); } } } static void check_unused_servers(PgPool *pool, struct StatList *slist, bool idle_test) { usec_t now = get_cached_time(); struct List *item, *tmp; usec_t idle, age; PgSocket *server; usec_t lifetime_kill_gap = 0; /* * Calculate the time that disconnects because of server_lifetime * must be separated. This avoids the need to re-launch lot * of connections together. */ if (pool->db->pool_size > 0) lifetime_kill_gap = cf_server_lifetime / pool->db->pool_size; /* disconnect idle servers if needed */ statlist_for_each_safe(item, slist, tmp) { server = container_of(item, PgSocket, head); age = now - server->connect_time; idle = now - server->request_time; if (server->close_needed) { disconnect_server(server, true, "database configuration changed"); } else if (server->state == SV_IDLE && !server->ready) { disconnect_server(server, true, "SV_IDLE server got dirty"); } else if (server->state == SV_USED && !server->ready) { disconnect_server(server, true, "SV_USED server got dirty"); } else if (cf_server_idle_timeout > 0 && idle > cf_server_idle_timeout && (cf_min_pool_size == 0 || pool_connected_server_count(pool) > cf_min_pool_size)) { disconnect_server(server, true, "server idle timeout"); } else if (age >= cf_server_lifetime) { if (pool->last_lifetime_disconnect + lifetime_kill_gap <= now) { disconnect_server(server, true, "server lifetime over"); pool->last_lifetime_disconnect = now; } } else if (cf_pause_mode == P_PAUSE) { disconnect_server(server, true, "pause mode"); } else if (idle_test && *cf_server_check_query) { if (idle > cf_server_check_delay) change_server_state(server, SV_USED); } } } /* * Check pool size, close conns if too many. Makes pooler * react faster to the case when admin decreased pool size. */ static void check_pool_size(PgPool *pool) { PgSocket *server; int cur = pool_connected_server_count(pool); int many = cur - (pool->db->pool_size + pool->db->res_pool_size); Assert(pool->db->pool_size >= 0); while (many > 0) { server = first_socket(&pool->used_server_list); if (!server) server = first_socket(&pool->idle_server_list); if (!server) break; disconnect_server(server, true, "too many servers in the pool"); many--; cur--; } /* launch extra connections to satisfy min_pool_size */ if (cur < cf_min_pool_size && cur < pool->db->pool_size && cf_pause_mode == P_NONE && cf_reboot == 0 && pool_client_count(pool) > 0) { log_debug("Launching new connection to satisfy min_pool_size"); launch_new_connection(pool); } } /* maintain servers in a pool */ static void pool_server_maint(PgPool *pool) { struct List *item, *tmp; usec_t age, now = get_cached_time(); PgSocket *server; /* find and disconnect idle servers */ check_unused_servers(pool, &pool->used_server_list, 0); check_unused_servers(pool, &pool->tested_server_list, 0); check_unused_servers(pool, &pool->idle_server_list, 1); /* where query got did not get answer in query_timeout */ if (cf_query_timeout > 0 || cf_idle_transaction_timeout > 0) { statlist_for_each_safe(item, &pool->active_server_list, tmp) { server = container_of(item, PgSocket, head); Assert(server->state == SV_ACTIVE); if (server->ready) continue; age = now - server->link->request_time; if (cf_query_timeout > 0 && age > cf_query_timeout) { disconnect_server(server, true, "query timeout"); } else if (cf_idle_transaction_timeout > 0 && server->idle_tx && age > cf_idle_transaction_timeout) { disconnect_server(server, true, "idle transaction timeout"); } } } /* find connections that got connect, but could not log in */ if (cf_server_connect_timeout > 0) { statlist_for_each_safe(item, &pool->new_server_list, tmp) { server = container_of(item, PgSocket, head); Assert(server->state == SV_LOGIN); age = now - server->connect_time; if (age > cf_server_connect_timeout) disconnect_server(server, true, "connect timeout"); } } check_pool_size(pool); } static void cleanup_client_logins(void) { struct List *item, *tmp; PgSocket *client; usec_t age; usec_t now = get_cached_time(); if (cf_client_login_timeout <= 0) return; statlist_for_each_safe(item, &login_client_list, tmp) { client = container_of(item, PgSocket, head); age = now - client->connect_time; if (age > cf_client_login_timeout) disconnect_client(client, true, "client_login_timeout"); } } static void kill_database(PgDatabase *db); static void cleanup_inactive_autodatabases(void) { struct List *item, *tmp; PgDatabase *db; usec_t age; usec_t now = get_cached_time(); if (cf_autodb_idle_timeout <= 0) return; /* now kill the old ones */ statlist_for_each_safe(item, &autodatabase_idle_list, tmp) { db = container_of(item, PgDatabase, head); if (db->db_paused) continue; age = now - db->inactive_time; if (age > cf_autodb_idle_timeout) { kill_database(db); } else { break; } } } /* full-scale maintenance, done only occasionally */ static void do_full_maint(int sock, short flags, void *arg) { struct List *item, *tmp; PgPool *pool; PgDatabase *db; static unsigned int seq; seq++; /* * Avoid doing anything that may surprise other pgbouncer. */ if (cf_pause_mode == P_SUSPEND) goto skip_maint; statlist_for_each_safe(item, &pool_list, tmp) { pool = container_of(item, PgPool, head); if (pool->db->admin) continue; pool_server_maint(pool); pool_client_maint(pool); /* is autodb active? */ if (pool->db->db_auto && pool->db->inactive_time == 0) { if (pool_client_count(pool) > 0 || pool_server_count(pool) > 0) pool->db->active_stamp = seq; } } /* find inactive autodbs */ statlist_for_each_safe(item, &database_list, tmp) { db = container_of(item, PgDatabase, head); if (db->db_auto && db->inactive_time == 0) { if (db->active_stamp == seq) continue; db->inactive_time = get_cached_time(); statlist_remove(&database_list, &db->head); statlist_append(&autodatabase_idle_list, &db->head); } } cleanup_inactive_autodatabases(); cleanup_client_logins(); if (cf_shutdown == 1 && get_active_server_count() == 0) { log_info("server connections dropped, exiting"); cf_shutdown = 2; event_loopbreak(); return; } if (cf_auth_type >= AUTH_TRUST) loader_users_check(); adns_zone_cache_maint(adns); skip_maint: safe_evtimer_add(&full_maint_ev, &full_maint_period); } /* first-time initialization */ void janitor_setup(void) { /* launch maintenance */ evtimer_set(&full_maint_ev, do_full_maint, NULL); safe_evtimer_add(&full_maint_ev, &full_maint_period); } void kill_pool(PgPool *pool) { const char *reason = "database removed"; close_client_list(&pool->active_client_list, reason); close_client_list(&pool->waiting_client_list, reason); close_client_list(&pool->cancel_req_list, reason); close_server_list(&pool->active_server_list, reason); close_server_list(&pool->idle_server_list, reason); close_server_list(&pool->used_server_list, reason); 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); slab_free(pool_cache, pool); } static void kill_database(PgDatabase *db) { PgPool *pool; struct List *item, *tmp; log_warning("dropping database '%s' as it does not exist anymore or inactive auto-database", db->name); statlist_for_each_safe(item, &pool_list, tmp) { pool = container_of(item, PgPool, head); 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) free((void *)db->connect_query); if (db->inactive_time) { statlist_remove(&autodatabase_idle_list, &db->head); } else { statlist_remove(&database_list, &db->head); } aatree_destroy(&db->user_tree); slab_free(db_cache, db); } /* as [pgbouncer] section can be loaded after databases, there's need for review */ void config_postprocess(void) { struct List *item, *tmp; PgDatabase *db; statlist_for_each_safe(item, &database_list, tmp) { db = container_of(item, PgDatabase, head); if (db->db_dead) { kill_database(db); continue; } if (db->pool_size < 0) db->pool_size = cf_default_pool_size; if (db->res_pool_size < 0) db->res_pool_size = cf_res_pool_size; } } pgbouncer-1.7/src/system.c0000664000175000017500000000640412572127367012567 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Compat functions for OSes where libc does not provide them. */ #include "bouncer.h" #ifdef HAVE_SYS_PARAM_H #include #endif #ifdef HAVE_UCRED_H #include #endif #ifdef HAVE_SYS_UCRED_H #include #endif #ifdef HAVE_PWD_H #include #endif #ifdef HAVE_GRP_H #include #endif void change_user(const char *user) { const struct passwd *pw; gid_t gset[1]; /* check for a valid username */ pw = getpwnam(user); if (pw == NULL) fatal("could not find user '%s' to switch to", user); gset[0] = pw->pw_gid; if (getuid() == 0) { if (setgroups(1, gset) < 0) fatal_perror("failed to reset groups"); } if (setgid(pw->pw_gid) < 0 || setuid(pw->pw_uid) < 0) fatal_perror("failed to assume identity of user '%s'", user); if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid) fatal("setuid() failed to work"); } /* set permissions & mode for file */ void change_file_mode(const char *fn, mode_t mode, const char *user_name, const char *group_name) { int res; uid_t uid = -1; gid_t gid = -1; unsigned long val; char *end; /* user lookup */ if (user_name && user_name[0]) { const struct passwd *pw; val = strtoul(user_name, &end, 0); if (*end == 0) { uid = val; } else { /* check for a valid username */ pw = getpwnam(user_name); if (!pw) fatal("could not find user '%s': %s", user_name, strerror(errno)); uid = pw->pw_uid; } } /* group lookup */ if (group_name && group_name[0]) { struct group *gr; val = strtoul(group_name, &end, 0); if (*end == 0) { gid = val; } else { gr = getgrnam(group_name); if (!gr) fatal("could not find group '%s': %s", group_name, strerror(errno)); gid = gr->gr_gid; } } /* change user/group */ if (uid != (uid_t)-1 || gid != (gid_t)-1) { res = chown(fn, uid, gid); if (res != 0) { fatal("chown(%s, %d, %d) failed: %s", fn, uid, gid, strerror(errno)); } } /* change mode */ res = chmod(fn, mode); if (res != 0) { fatal("Failure to chmod(%s, 0%o): %s", fn, mode, strerror(errno)); } } /* * UNIX socket helper. */ bool check_unix_peer_name(int fd, const char *username) { int res; uid_t peer_uid = -1; gid_t peer_gid = -1; struct passwd *pw; res = getpeereid(fd, &peer_uid, &peer_gid); if (res < 0) return false; pw = getpwuid(peer_uid); if (!pw) return false; return strcmp(pw->pw_name, username) == 0; } pgbouncer-1.7/src/main.c0000664000175000017500000005362512617620356012172 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Launcher for all the rest. */ #include "bouncer.h" #include #include #include #include #ifdef HAVE_SYS_RESOURCE_H #include #endif static const char usage_str[] = "Usage: %s [OPTION]... config.ini\n" " -d, --daemon Run in background (as a daemon)\n" " -R, --restart Do a online restart\n" " -q, --quiet Run quietly\n" " -v, --verbose Increase verbosity\n" " -u, --user= Assume identity of \n" " -V, --version Show version\n" " -h, --help Show this help screen and exit\n"; static void usage(int err, char *exe) { printf(usage_str, basename(exe)); exit(err); } /* async dns handler */ struct DNSContext *adns; struct HBA *parsed_hba; /* * configuration storage */ int cf_daemon; int cf_pause_mode = P_NONE; int cf_shutdown; /* 1 - wait for queries to finish, 2 - shutdown immediately */ int cf_reboot; static char *cf_username; char *cf_config_file; char *cf_listen_addr; int cf_listen_port; int cf_listen_backlog; char *cf_unix_socket_dir; int cf_unix_socket_mode; char *cf_unix_socket_group; int cf_pool_mode = POOL_SESSION; /* sbuf config */ int cf_sbuf_len; int cf_sbuf_loopcnt; int cf_tcp_socket_buffer; #if defined(TCP_DEFER_ACCEPT) || defined(SO_ACCEPTFILTER) int cf_tcp_defer_accept = 1; #else int cf_tcp_defer_accept = 0; #endif int cf_tcp_keepalive; int cf_tcp_keepcnt; int cf_tcp_keepidle; int cf_tcp_keepintvl; int cf_auth_type = AUTH_MD5; char *cf_auth_file; char *cf_auth_hba_file; char *cf_auth_query; int cf_max_client_conn; int cf_default_pool_size; int cf_min_pool_size; int cf_res_pool_size; usec_t cf_res_pool_timeout; int cf_max_db_connections; int cf_max_user_connections; char *cf_server_reset_query; int cf_server_reset_query_always; char *cf_server_check_query; usec_t cf_server_check_delay; int cf_server_round_robin; int cf_disable_pqexec; usec_t cf_dns_max_ttl; usec_t cf_dns_nxdomain_ttl; usec_t cf_dns_zone_check_period; unsigned int cf_max_packet_size; char *cf_ignore_startup_params; char *cf_autodb_connstr; /* here is "" different from NULL */ usec_t cf_autodb_idle_timeout; usec_t cf_server_lifetime; usec_t cf_server_idle_timeout; usec_t cf_server_connect_timeout; usec_t cf_server_login_retry; usec_t cf_query_timeout; usec_t cf_query_wait_timeout; usec_t cf_client_idle_timeout; usec_t cf_client_login_timeout; usec_t cf_idle_transaction_timeout; usec_t cf_suspend_timeout; usec_t g_suspend_start; char *cf_pidfile; char *cf_jobname; char *cf_admin_users; char *cf_stats_users; int cf_stats_period; int cf_log_connections; int cf_log_disconnections; int cf_log_pooler_errors; int cf_application_name_add_host; int cf_client_tls_sslmode; char *cf_client_tls_protocols; char *cf_client_tls_ca_file; char *cf_client_tls_cert_file; char *cf_client_tls_key_file; char *cf_client_tls_ciphers; char *cf_client_tls_dheparams; char *cf_client_tls_ecdhecurve; int cf_server_tls_sslmode; char *cf_server_tls_protocols; char *cf_server_tls_ca_file; char *cf_server_tls_cert_file; char *cf_server_tls_key_file; char *cf_server_tls_ciphers; /* * config file description */ static bool set_defer_accept(struct CfValue *cv, const char *val); #define DEFER_OPS {set_defer_accept, cf_get_int} static const struct CfLookup auth_type_map[] = { { "any", AUTH_ANY }, { "trust", AUTH_TRUST }, { "plain", AUTH_PLAIN }, { "md5", AUTH_MD5 }, { "cert", AUTH_CERT }, { "hba", AUTH_HBA }, { NULL } }; const struct CfLookup pool_mode_map[] = { { "session", POOL_SESSION }, { "transaction", POOL_TX }, { "statement", POOL_STMT }, { NULL } }; const struct CfLookup sslmode_map[] = { { "disabled", SSLMODE_DISABLED }, { "allow", SSLMODE_ALLOW }, { "prefer", SSLMODE_PREFER }, { "require", SSLMODE_REQUIRE }, { "verify-ca", SSLMODE_VERIFY_CA }, { "verify-full", SSLMODE_VERIFY_FULL }, { NULL } }; static const struct CfKey bouncer_params [] = { CF_ABS("job_name", CF_STR, cf_jobname, CF_NO_RELOAD, "pgbouncer"), #ifdef WIN32 CF_ABS("service_name", CF_STR, cf_jobname, CF_NO_RELOAD, NULL), /* alias for job_name */ #endif CF_ABS("conffile", CF_STR, cf_config_file, 0, NULL), CF_ABS("logfile", CF_STR, cf_logfile, 0, ""), CF_ABS("pidfile", CF_STR, cf_pidfile, CF_NO_RELOAD, ""), CF_ABS("listen_addr", CF_STR, cf_listen_addr, CF_NO_RELOAD, ""), CF_ABS("listen_port", CF_INT, cf_listen_port, CF_NO_RELOAD, "6432"), CF_ABS("listen_backlog", CF_INT, cf_listen_backlog, CF_NO_RELOAD, "128"), #ifndef WIN32 CF_ABS("unix_socket_dir", CF_STR, cf_unix_socket_dir, CF_NO_RELOAD, "/tmp"), CF_ABS("unix_socket_mode", CF_INT, cf_unix_socket_mode, CF_NO_RELOAD, "0777"), CF_ABS("unix_socket_group", CF_STR, cf_unix_socket_group, CF_NO_RELOAD, ""), #endif CF_ABS("auth_type", CF_LOOKUP(auth_type_map), cf_auth_type, 0, "md5"), CF_ABS("auth_file", CF_STR, cf_auth_file, 0, "unconfigured_file"), CF_ABS("auth_hba_file", CF_STR, cf_auth_hba_file, 0, ""), CF_ABS("auth_query", CF_STR, cf_auth_query, 0, "SELECT usename, passwd FROM pg_shadow WHERE usename=$1"), CF_ABS("pool_mode", CF_LOOKUP(pool_mode_map), cf_pool_mode, 0, "session"), CF_ABS("max_client_conn", CF_INT, cf_max_client_conn, 0, "100"), CF_ABS("default_pool_size", CF_INT, cf_default_pool_size, 0, "20"), CF_ABS("min_pool_size", CF_INT, cf_min_pool_size, 0, "0"), CF_ABS("reserve_pool_size", CF_INT, cf_res_pool_size, 0, "0"), CF_ABS("reserve_pool_timeout", CF_TIME_USEC, cf_res_pool_timeout, 0, "5"), CF_ABS("max_db_connections", CF_INT, cf_max_db_connections, 0, "0"), CF_ABS("max_user_connections", CF_INT, cf_max_user_connections, 0, "0"), CF_ABS("syslog", CF_INT, cf_syslog, 0, "0"), CF_ABS("syslog_facility", CF_STR, cf_syslog_facility, 0, "daemon"), CF_ABS("syslog_ident", CF_STR, cf_syslog_ident, 0, "pgbouncer"), #ifndef WIN32 CF_ABS("user", CF_STR, cf_username, CF_NO_RELOAD, NULL), #endif CF_ABS("autodb_idle_timeout", CF_TIME_USEC, cf_autodb_idle_timeout, 0, "3600"), CF_ABS("server_reset_query", CF_STR, cf_server_reset_query, 0, "DISCARD ALL"), CF_ABS("server_reset_query_always", CF_INT, cf_server_reset_query_always, 0, "0"), CF_ABS("server_check_query", CF_STR, cf_server_check_query, 0, "select 1"), CF_ABS("server_check_delay", CF_TIME_USEC, cf_server_check_delay, 0, "30"), CF_ABS("query_timeout", CF_TIME_USEC, cf_query_timeout, 0, "0"), CF_ABS("query_wait_timeout", CF_TIME_USEC, cf_query_wait_timeout, 0, "120"), CF_ABS("client_idle_timeout", CF_TIME_USEC, cf_client_idle_timeout, 0, "0"), CF_ABS("client_login_timeout", CF_TIME_USEC, cf_client_login_timeout, 0, "60"), CF_ABS("idle_transaction_timeout", CF_TIME_USEC, cf_idle_transaction_timeout, 0, "0"), CF_ABS("server_lifetime", CF_TIME_USEC, cf_server_lifetime, 0, "3600"), CF_ABS("server_idle_timeout", CF_TIME_USEC, cf_server_idle_timeout, 0, "600"), CF_ABS("server_connect_timeout", CF_TIME_USEC, cf_server_connect_timeout, 0, "15"), CF_ABS("server_login_retry", CF_TIME_USEC, cf_server_login_retry, 0, "15"), CF_ABS("server_round_robin", CF_INT, cf_server_round_robin, 0, "0"), CF_ABS("suspend_timeout", CF_TIME_USEC, cf_suspend_timeout, 0, "10"), CF_ABS("ignore_startup_parameters", CF_STR, cf_ignore_startup_params, 0, ""), CF_ABS("disable_pqexec", CF_INT, cf_disable_pqexec, CF_NO_RELOAD, "0"), CF_ABS("dns_max_ttl", CF_TIME_USEC, cf_dns_max_ttl, 0, "15"), CF_ABS("dns_nxdomain_ttl", CF_TIME_USEC, cf_dns_nxdomain_ttl, 0, "15"), CF_ABS("dns_zone_check_period", CF_TIME_USEC, cf_dns_zone_check_period, 0, "0"), CF_ABS("max_packet_size", CF_UINT, cf_max_packet_size, 0, "2147483647"), CF_ABS("pkt_buf", CF_INT, cf_sbuf_len, CF_NO_RELOAD, "4096"), CF_ABS("sbuf_loopcnt", CF_INT, cf_sbuf_loopcnt, 0, "5"), CF_ABS("tcp_defer_accept", DEFER_OPS, cf_tcp_defer_accept, 0, NULL), CF_ABS("tcp_socket_buffer", CF_INT, cf_tcp_socket_buffer, 0, "0"), CF_ABS("tcp_keepalive", CF_INT, cf_tcp_keepalive, 0, "1"), CF_ABS("tcp_keepcnt", CF_INT, cf_tcp_keepcnt, 0, "0"), CF_ABS("tcp_keepidle", CF_INT, cf_tcp_keepidle, 0, "0"), CF_ABS("tcp_keepintvl", CF_INT, cf_tcp_keepintvl, 0, "0"), CF_ABS("verbose", CF_INT, cf_verbose, 0, NULL), CF_ABS("admin_users", CF_STR, cf_admin_users, 0, ""), CF_ABS("stats_users", CF_STR, cf_stats_users, 0, ""), CF_ABS("stats_period", CF_INT, cf_stats_period, 0, "60"), CF_ABS("log_connections", CF_INT, cf_log_connections, 0, "1"), CF_ABS("log_disconnections", CF_INT, cf_log_disconnections, 0, "1"), CF_ABS("log_pooler_errors", CF_INT, cf_log_pooler_errors, 0, "1"), CF_ABS("application_name_add_host", CF_INT, cf_application_name_add_host, 0, "0"), CF_ABS("client_tls_sslmode", CF_LOOKUP(sslmode_map), cf_client_tls_sslmode, CF_NO_RELOAD, "disabled"), CF_ABS("client_tls_ca_file", CF_STR, cf_client_tls_ca_file, CF_NO_RELOAD, ""), CF_ABS("client_tls_cert_file", CF_STR, cf_client_tls_cert_file, CF_NO_RELOAD, ""), CF_ABS("client_tls_key_file", CF_STR, cf_client_tls_key_file, CF_NO_RELOAD, ""), CF_ABS("client_tls_protocols", CF_STR, cf_client_tls_protocols, CF_NO_RELOAD, "all"), CF_ABS("client_tls_ciphers", CF_STR, cf_client_tls_ciphers, CF_NO_RELOAD, "fast"), CF_ABS("client_tls_dheparams", CF_STR, cf_client_tls_dheparams, CF_NO_RELOAD, "auto"), CF_ABS("client_tls_ecdhcurve", CF_STR, cf_client_tls_ecdhecurve, CF_NO_RELOAD, "auto"), CF_ABS("server_tls_sslmode", CF_LOOKUP(sslmode_map), cf_server_tls_sslmode, CF_NO_RELOAD, "disabled"), CF_ABS("server_tls_ca_file", CF_STR, cf_server_tls_ca_file, CF_NO_RELOAD, ""), CF_ABS("server_tls_cert_file", CF_STR, cf_server_tls_cert_file, CF_NO_RELOAD, ""), CF_ABS("server_tls_key_file", CF_STR, cf_server_tls_key_file, CF_NO_RELOAD, ""), CF_ABS("server_tls_protocols", CF_STR, cf_server_tls_protocols, CF_NO_RELOAD, "all"), CF_ABS("server_tls_ciphers", CF_STR, cf_server_tls_ciphers, CF_NO_RELOAD, "fast"), {NULL} }; static const struct CfSect config_sects [] = { { .sect_name = "pgbouncer", .key_list = bouncer_params, }, { .sect_name = "databases", .set_key = parse_database, }, { .sect_name = "users", .set_key = parse_user, }, { .sect_name = NULL, } }; static struct CfContext main_config = { config_sects, }; bool set_config_param(const char *key, const char *val) { return cf_set(&main_config, "pgbouncer", key, val); } void config_for_each(void (*param_cb)(void *arg, const char *name, const char *val, bool reloadable), void *arg) { const struct CfKey *k = bouncer_params; char buf[256]; bool reloadable; const char *val; int ro = CF_NO_RELOAD | CF_READONLY; for (; k->key_name; k++) { val = cf_get(&main_config, "pgbouncer", k->key_name, buf, sizeof(buf)); reloadable = (k->flags & ro) == 0; param_cb(arg, k->key_name, val, reloadable); } } static bool set_defer_accept(struct CfValue *cv, const char *val) { int *p = cv->value_p; bool ok; int oldval = *p; ok = cf_set_int(cv, val); if (ok && !!oldval != !!*p) pooler_tune_accept(*p); return ok; } static void set_dbs_dead(bool flag) { struct List *item; PgDatabase *db; statlist_for_each(item, &database_list) { db = container_of(item, PgDatabase, head); if (db->admin) continue; if (db->db_auto) continue; db->db_dead = flag; } } /* config loading, tries to be tolerant to errors */ void load_config(void) { static bool loaded = false; bool ok; set_dbs_dead(true); /* actual loading */ ok = cf_load_file(&main_config, cf_config_file); if (ok) { /* load users if needed */ if (cf_auth_type >= AUTH_TRUST) loader_users_check(); loaded = true; } else if (!loaded) { die("Cannot load config file"); } else { log_warning("Config file loading failed"); /* if ini file missing, don't kill anybody */ set_dbs_dead(false); } if (cf_auth_type == AUTH_HBA) { struct HBA *hba = hba_load_rules(cf_auth_hba_file); if (hba) { if (parsed_hba) hba_free(parsed_hba); parsed_hba = hba; } } /* reset pool_size, kill dbs */ config_postprocess(); /* reopen logfile */ if (main_config.loaded) reset_logging(); } /* * signal handling. * * handle_* functions are not actual signal handlers but called from * event_loop() so they have no restrictions what they can do. */ static struct event ev_sigterm; static struct event ev_sigint; static void handle_sigterm(int sock, short flags, void *arg) { log_info("Got SIGTERM, fast exit"); /* pidfile cleanup happens via atexit() */ exit(1); } static void handle_sigint(int sock, short flags, void *arg) { log_info("Got SIGINT, shutting down"); if (cf_reboot) fatal("Takeover was in progress, going down immediately"); if (cf_pause_mode == P_SUSPEND) fatal("Suspend was in progress, going down immediately"); cf_pause_mode = P_PAUSE; cf_shutdown = 1; } #ifndef WIN32 static struct event ev_sigusr1; static struct event ev_sigusr2; static struct event ev_sighup; static void handle_sigusr1(int sock, short flags, void *arg) { if (cf_pause_mode == P_NONE) { log_info("Got SIGUSR1, pausing all activity"); cf_pause_mode = P_PAUSE; } else { log_info("Got SIGUSR1, but already paused/suspended"); } } static void handle_sigusr2(int sock, short flags, void *arg) { switch (cf_pause_mode) { case P_SUSPEND: log_info("Got SIGUSR2, continuing from SUSPEND"); resume_all(); cf_pause_mode = P_NONE; break; case P_PAUSE: log_info("Got SIGUSR2, continuing from PAUSE"); cf_pause_mode = P_NONE; break; case P_NONE: log_info("Got SIGUSR1, but not paused/suspended"); } /* avoid surprise later if cf_shutdown stays set */ if (cf_shutdown) { log_info("Canceling shutdown"); cf_shutdown = 0; } } static void handle_sighup(int sock, short flags, void *arg) { log_info("Got SIGHUP re-reading config"); load_config(); } #endif static void signal_setup(void) { int err; #ifndef WIN32 sigset_t set; /* block SIGPIPE */ sigemptyset(&set); sigaddset(&set, SIGPIPE); err = sigprocmask(SIG_BLOCK, &set, NULL); if (err < 0) fatal_perror("sigprocmask"); /* install handlers */ signal_set(&ev_sigusr1, SIGUSR1, handle_sigusr1, NULL); err = signal_add(&ev_sigusr1, NULL); if (err < 0) fatal_perror("signal_add"); signal_set(&ev_sigusr2, SIGUSR2, handle_sigusr2, NULL); err = signal_add(&ev_sigusr2, NULL); if (err < 0) fatal_perror("signal_add"); signal_set(&ev_sighup, SIGHUP, handle_sighup, NULL); err = signal_add(&ev_sighup, NULL); if (err < 0) fatal_perror("signal_add"); #endif signal_set(&ev_sigterm, SIGTERM, handle_sigterm, NULL); err = signal_add(&ev_sigterm, NULL); if (err < 0) fatal_perror("signal_add"); signal_set(&ev_sigint, SIGINT, handle_sigint, NULL); err = signal_add(&ev_sigint, NULL); if (err < 0) fatal_perror("signal_add"); } /* * daemon mode */ static void go_daemon(void) { int pid, fd; if (!cf_pidfile[0]) fatal("daemon needs pidfile configured"); /* don't log to stdout anymore */ cf_quiet = 1; /* send stdin, stdout, stderr to /dev/null */ fd = open("/dev/null", O_RDWR); if (fd < 0) fatal_perror("/dev/null"); dup2(fd, 0); dup2(fd, 1); dup2(fd, 2); if (fd > 2) close(fd); /* fork new process */ pid = fork(); if (pid < 0) fatal_perror("fork"); if (pid > 0) _exit(0); /* create new session */ pid = setsid(); if (pid < 0) fatal_perror("setsid"); /* fork again to avoid being session leader */ pid = fork(); if (pid < 0) fatal_perror("fork"); if (pid > 0) _exit(0); } /* * pidfile management. */ static void remove_pidfile(void) { if (!cf_pidfile[0]) return; unlink(cf_pidfile); } static void check_pidfile(void) { char buf[128 + 1]; struct stat st; pid_t pid = 0; int fd, res; if (!cf_pidfile[0]) return; /* check if pidfile exists */ if (stat(cf_pidfile, &st) < 0) { if (errno != ENOENT) fatal_perror("stat"); return; } /* read old pid */ fd = open(cf_pidfile, O_RDONLY); if (fd < 0) goto locked_pidfile; res = read(fd, buf, sizeof(buf) - 1); close(fd); if (res <= 0) goto locked_pidfile; /* parse pid */ buf[res] = 0; pid = atol(buf); if (pid <= 0) goto locked_pidfile; /* check if running */ if (kill(pid, 0) >= 0) goto locked_pidfile; if (errno != ESRCH) goto locked_pidfile; /* seems the pidfile is not in use */ log_info("Stale pidfile, removing"); remove_pidfile(); return; locked_pidfile: fatal("pidfile exists, another instance running?"); } static void write_pidfile(void) { char buf[64]; pid_t pid; int res, fd; if (!cf_pidfile[0]) return; pid = getpid(); snprintf(buf, sizeof(buf), "%u\n", (unsigned)pid); fd = open(cf_pidfile, O_WRONLY | O_CREAT | O_EXCL, 0644); if (fd < 0) fatal_perror("%s", cf_pidfile); res = safe_write(fd, buf, strlen(buf)); if (res < 0) fatal_perror("%s", cf_pidfile); close(fd); /* only remove when we have it actually written */ atexit(remove_pidfile); } /* just print out max files, in the future may warn if something is off */ static void check_limits(void) { struct rlimit lim; int total_users = statlist_count(&user_list); int fd_count; int err; struct List *item; PgDatabase *db; log_noise("event: %d, SBuf: %d, PgSocket: %d, IOBuf: %d", (int)sizeof(struct event), (int)sizeof(SBuf), (int)sizeof(PgSocket), (int)IOBUF_SIZE); /* load limits */ err = getrlimit(RLIMIT_NOFILE, &lim); if (err < 0) { log_error("could not get RLIMIT_NOFILE: %s", strerror(errno)); return; } /* calculate theoretical max, +10 is just in case */ fd_count = cf_max_client_conn + 10; statlist_for_each(item, &database_list) { db = container_of(item, PgDatabase, head); if (db->forced_user) fd_count += db->pool_size; else fd_count += db->pool_size * total_users; } log_info("File descriptor limit: %d (H:%d), max_client_conn: %d, max fds possible: %d", (int)lim.rlim_cur, (int)lim.rlim_max, cf_max_client_conn, fd_count); } static bool check_old_process_unix(void) { struct sockaddr_un sa_un; socklen_t len = sizeof(sa_un); int domain = AF_UNIX; int res, fd; if (!cf_unix_socket_dir || !*cf_unix_socket_dir) return false; memset(&sa_un, 0, len); sa_un.sun_family = domain; snprintf(sa_un.sun_path, sizeof(sa_un.sun_path), "%s/.s.PGSQL.%d", cf_unix_socket_dir, cf_listen_port); fd = socket(domain, SOCK_STREAM, 0); if (fd < 0) fatal_perror("cannot create socket"); res = safe_connect(fd, (struct sockaddr *)&sa_un, len); safe_close(fd); if (res < 0) return false; return true; } static void main_loop_once(void) { int err; reset_time_cache(); err = event_loop(EVLOOP_ONCE); if (err < 0) { if (errno != EINTR) log_warning("event_loop failed: %s", strerror(errno)); } per_loop_maint(); reuse_just_freed_objects(); rescue_timers(); per_loop_pooler_maint(); if (adns) adns_per_loop(adns); } static void takeover_part1(void) { /* use temporary libevent base */ void *evtmp = event_init(); if (!cf_unix_socket_dir || !*cf_unix_socket_dir) fatal("cannot reboot if unix dir not configured"); takeover_init(); while (cf_reboot) main_loop_once(); event_base_free(evtmp); } static void dns_setup(void) { if (adns) return; adns = adns_create_context(); if (!adns) fatal_perror("dns setup failed"); } /* boot everything */ int main(int argc, char *argv[]) { int c; bool did_takeover = false; char *arg_username = NULL; int long_idx; static const struct option long_options[] = { {"quiet", no_argument, NULL, 'q'}, {"verbose", no_argument, NULL, 'v'}, {"help", no_argument, NULL, 'h'}, {"daemon", no_argument, NULL, 'd'}, {"version", no_argument, NULL, 'V'}, {"reboot", no_argument, NULL, 'R'}, {"user", required_argument, NULL, 'u'}, {NULL, 0, NULL, 0} }; setprogname(basename(argv[0])); /* parse cmdline */ while ((c = getopt_long(argc, argv, "qvhdVRu:", long_options, &long_idx)) != -1) { switch (c) { case 'R': cf_reboot = 1; break; case 'v': cf_verbose++; break; case 'V': printf("%s\n", FULLVER); return 0; case 'd': cf_daemon = 1; break; case 'q': cf_quiet = 1; break; case 'u': arg_username = optarg; break; case 'h': usage(0, argv[0]); default: usage(1, argv[0]); } } if (optind + 1 != argc) { fprintf(stderr, "Need config file. See pgbouncer -h for usage.\n"); exit(1); } cf_config_file = xstrdup(argv[optind]); init_objects(); load_config(); main_config.loaded = true; init_caches(); logging_prefix_cb = log_socket_prefix; sbuf_tls_setup(); /* prefer cmdline over config for username */ if (arg_username) { if (cf_username) free(cf_username); cf_username = xstrdup(arg_username); } /* switch user is needed */ if (cf_username && *cf_username) change_user(cf_username); /* disallow running as root */ if (getuid() == 0) fatal("PgBouncer should not run as root"); /* need to do that after loading config */ check_limits(); admin_setup(); if (cf_reboot) { if (check_old_process_unix()) { takeover_part1(); did_takeover = true; } else { log_info("old process not found, try to continue normally"); cf_reboot = 0; check_pidfile(); } } else { if (check_old_process_unix()) fatal("unix socket is in use, cannot continue"); check_pidfile(); } if (cf_daemon) go_daemon(); /* initialize subsystems, order important */ srandom(time(NULL) ^ getpid()); if (!event_init()) fatal("event_init() failed"); dns_setup(); signal_setup(); janitor_setup(); stats_setup(); if (did_takeover) { takeover_finish(); } else { pooler_setup(); } write_pidfile(); log_info("process up: %s, libevent %s (%s), adns: %s", PACKAGE_STRING, event_get_version(), event_get_method(), adns_get_backend()); /* main loop */ while (cf_shutdown < 2) main_loop_once(); return 0; } pgbouncer-1.7/src/pooler.c0000664000175000017500000002641312627600727012542 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Handling of pooler listening sockets */ #include "bouncer.h" #include struct ListenSocket { struct List node; int fd; bool active; struct event ev; PgAddr addr; }; static STATLIST(sock_list); /* hints for getaddrinfo(listen_addr) */ static const struct addrinfo hints = { .ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM, .ai_protocol = IPPROTO_TCP, .ai_flags = AI_PASSIVE, }; /* should listening sockets be active or suspended? */ static bool need_active = false; /* is it actually active or suspended? */ static bool pooler_active = false; /* on accept() failure sleep 5 seconds */ static struct event ev_err; static struct timeval err_timeout = {5, 0}; static void tune_accept(int sock, bool on); /* atexit() cleanup func */ static void cleanup_sockets(void) { struct ListenSocket *ls; struct List *el; /* avoid cleanup if exit() while suspended */ if (cf_pause_mode == P_SUSPEND) return; while ((el = statlist_pop(&sock_list)) != NULL) { ls = container_of(el, struct ListenSocket, node); if (ls->fd > 0) { safe_close(ls->fd); ls->fd = 0; } if (pga_is_unix(&ls->addr)) { char buf[sizeof(struct sockaddr_un) + 20]; snprintf(buf, sizeof(buf), "%s/.s.PGSQL.%d", cf_unix_socket_dir, cf_listen_port); unlink(buf); } statlist_remove(&sock_list, &ls->node); free(ls); } } /* * initialize another listening socket. */ static bool add_listen(int af, const struct sockaddr *sa, int salen) { struct ListenSocket *ls; int sock, res; char buf[128]; const char *errpos; log_debug("add_listen: %s", sa2str(sa, buf, sizeof(buf))); /* create socket */ errpos = "socket"; sock = socket(af, SOCK_STREAM, 0); if (sock < 0) goto failed; /* SO_REUSEADDR behaviour it default in WIN32. */ #ifndef WIN32 /* relaxed binding */ if (af != AF_UNIX) { int val = 1; errpos = "setsockopt"; res = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); if (res < 0) goto failed; } #endif #ifdef IPV6_V6ONLY /* avoid ipv6 socket's attempt to takeover ipv4 port */ if (af == AF_INET6) { int val = 1; errpos = "setsockopt/IPV6_V6ONLY"; res = setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)); if (res < 0) goto failed; } #endif /* bind it */ errpos = "bind"; res = bind(sock, sa, salen); if (res < 0) goto failed; /* set common options */ errpos = "tune_socket"; if (!tune_socket(sock, (af == AF_UNIX))) goto failed; /* finally, accept connections */ errpos = "listen"; res = listen(sock, cf_listen_backlog); if (res < 0) goto failed; errpos = "calloc"; ls = calloc(1, sizeof(*ls)); if (!ls) goto failed; list_init(&ls->node); ls->fd = sock; if (sa->sa_family == AF_UNIX) { pga_set(&ls->addr, AF_UNIX, cf_listen_port); } else { pga_copy(&ls->addr, sa); } if (af == AF_UNIX) { struct sockaddr_un *un = (struct sockaddr_un *)sa; change_file_mode(un->sun_path, cf_unix_socket_mode, NULL, cf_unix_socket_group); } else { tune_accept(sock, cf_tcp_defer_accept); } log_info("listening on %s", sa2str(sa, buf, sizeof(buf))); statlist_append(&sock_list, &ls->node); return true; failed: log_warning("Cannot listen on %s: %s(): %s", sa2str(sa, buf, sizeof(buf)), errpos, strerror(errno)); if (sock >= 0) safe_close(sock); return false; } static void create_unix_socket(const char *socket_dir, int listen_port) { struct sockaddr_un un; int res; char lockfile[sizeof(struct sockaddr_un) + 10]; struct stat st; /* fill sockaddr struct */ memset(&un, 0, sizeof(un)); un.sun_family = AF_UNIX; snprintf(un.sun_path, sizeof(un.sun_path), "%s/.s.PGSQL.%d", socket_dir, listen_port); /* check for lockfile */ snprintf(lockfile, sizeof(lockfile), "%s.lock", un.sun_path); res = lstat(lockfile, &st); if (res == 0) fatal("unix port %d is in use", listen_port); /* expect old bouncer gone */ unlink(un.sun_path); add_listen(AF_UNIX, (const struct sockaddr *)&un, sizeof(un)); } /* * Notify pooler only when also data is arrived. * * optval specifies how long after connection attempt to wait for data. * * Related to tcp_synack_retries sysctl, default 5 (corresponds 180 secs). * * SO_ACCEPTFILTER needs to be set after listen(), maybe TCP_DEFER_ACCEPT too. */ static void tune_accept(int sock, bool on) { const char *act = on ? "install" : "uninstall"; int res = 0; #ifdef TCP_DEFER_ACCEPT int val = 45; /* FIXME: proper value */ socklen_t vlen = sizeof(val); res = getsockopt(sock, IPPROTO_TCP, TCP_DEFER_ACCEPT, &val, &vlen); log_noise("old TCP_DEFER_ACCEPT on %d = %d", sock, val); val = on ? 1 : 0; log_noise("%s TCP_DEFER_ACCEPT on %d", act, sock); res = setsockopt(sock, IPPROTO_TCP, TCP_DEFER_ACCEPT, &val, sizeof(val)); #else #if 0 #ifdef SO_ACCEPTFILTER struct accept_filter_arg af, *afp = on ? &af : NULL; socklen_t af_len = on ? sizeof(af) : 0; memset(&af, 0, sizeof(af)); strcpy(af.af_name, "dataready"); log_noise("%s SO_ACCEPTFILTER on %d", act, sock); res = setsockopt(sock, SOL_SOCKET, SO_ACCEPTFILTER, afp, af_len); #endif #endif #endif if (res < 0) log_warning("tune_accept: %s TCP_DEFER_ACCEPT/SO_ACCEPTFILTER: %s", act, strerror(errno)); } void pooler_tune_accept(bool on) { struct List *el; struct ListenSocket *ls; statlist_for_each(el, &sock_list) { ls = container_of(el, struct ListenSocket, node); if (!pga_is_unix(&ls->addr)) tune_accept(ls->fd, on); } } static void err_wait_func(int sock, short flags, void *arg) { if (cf_pause_mode != P_SUSPEND) resume_pooler(); } static const char *addrpair(const PgAddr *src, const PgAddr *dst) { static char ip1buf[PGADDR_BUF], ip2buf[PGADDR_BUF], buf[2*PGADDR_BUF + 16]; const char *ip1, *ip2; if (pga_is_unix(src)) return "unix->unix"; ip1 = pga_ntop(src, ip1buf, sizeof(ip1buf)); ip2 = pga_ntop(src, ip2buf, sizeof(ip2buf)); snprintf(buf, sizeof(buf), "%s:%d -> %s:%d", ip1, pga_port(src), ip2, pga_port(dst)); return buf; } static const char *conninfo(const PgSocket *sk) { if (is_server_socket(sk)) { return addrpair(&sk->local_addr, &sk->remote_addr); } else { return addrpair(&sk->remote_addr, &sk->local_addr); } } /* got new connection, associate it with client struct */ static void pool_accept(int sock, short flags, void *arg) { struct ListenSocket *ls = arg; int fd; PgSocket *client; union { struct sockaddr_in in; struct sockaddr_in6 in6; struct sockaddr_un un; struct sockaddr sa; } raddr; socklen_t len = sizeof(raddr); bool is_unix = pga_is_unix(&ls->addr); if(!(flags & EV_READ)) { log_warning("No EV_READ in pool_accept"); return; } loop: /* get fd */ fd = safe_accept(sock, &raddr.sa, &len); if (fd < 0) { if (errno == EAGAIN) return; else if (errno == ECONNABORTED) return; /* * probably fd limit, pointless to try often * wait a bit, hope that admin resolves somehow */ log_error("accept() failed: %s", strerror(errno)); evtimer_set(&ev_err, err_wait_func, NULL); safe_evtimer_add(&ev_err, &err_timeout); suspend_pooler(); return; } log_noise("new fd from accept=%d", fd); if (is_unix) { client = accept_client(fd, true); } else { client = accept_client(fd, false); } if (client) slog_debug(client, "P: got connection: %s", conninfo(client)); /* * there may be several clients waiting, * avoid context switch by looping */ goto loop; } bool use_pooler_socket(int sock, bool is_unix) { struct ListenSocket *ls; int res; char buf[PGADDR_BUF]; if (!tune_socket(sock, is_unix)) return false; ls = calloc(1, sizeof(*ls)); if (!ls) return false; ls->fd = sock; if (is_unix) { pga_set(&ls->addr, AF_UNIX, cf_listen_port); } else { struct sockaddr_storage ss; socklen_t len = sizeof(ss); res = getsockname(sock, (struct sockaddr *)&ss, &len); if (res < 0) { log_error("getsockname failed"); free(ls); return false; } pga_copy(&ls->addr, (struct sockaddr *)&ss); } log_info("got pooler socket: %s", pga_str(&ls->addr, buf, sizeof(buf))); statlist_append(&sock_list, &ls->node); return true; } void suspend_pooler(void) { struct List *el; struct ListenSocket *ls; need_active = false; statlist_for_each(el, &sock_list) { ls = container_of(el, struct ListenSocket, node); if (!ls->active) continue; if (event_del(&ls->ev) < 0) { log_warning("suspend_pooler, event_del: %s", strerror(errno)); return; } ls->active = false; } pooler_active = false; } void resume_pooler(void) { struct List *el; struct ListenSocket *ls; need_active = true; statlist_for_each(el, &sock_list) { ls = container_of(el, struct ListenSocket, node); if (ls->active) continue; event_set(&ls->ev, ls->fd, EV_READ | EV_PERSIST, pool_accept, ls); if (event_add(&ls->ev, NULL) < 0) { log_warning("event_add failed: %s", strerror(errno)); return; } ls->active = true; } pooler_active = true; } /* retry previously failed suspend_pooler() / resume_pooler() */ void per_loop_pooler_maint(void) { if (need_active && !pooler_active) resume_pooler(); else if (!need_active && pooler_active) suspend_pooler(); } static bool parse_addr(void *arg, const char *addr) { int res; char service[64]; struct addrinfo *ai, *gaires = NULL; bool ok; if (!*addr) return true; if (strcmp(addr, "*") == 0) addr = NULL; snprintf(service, sizeof(service), "%d", cf_listen_port); res = getaddrinfo(addr, service, &hints, &gaires); if (res != 0) { fatal("getaddrinfo('%s', '%d') = %s [%d]", addr ? addr : "*", cf_listen_port, gai_strerror(res), res); } for (ai = gaires; ai; ai = ai->ai_next) { ok = add_listen(ai->ai_family, ai->ai_addr, ai->ai_addrlen); /* it's unclear whether all or only first result should be used */ if (0 && ok) break; } freeaddrinfo(gaires); return true; } /* listen on socket - should happen after all other initializations */ void pooler_setup(void) { bool ok; static int init_done = 0; if (!init_done) { /* remove socket on shutdown */ atexit(cleanup_sockets); init_done = 1; } ok = parse_word_list(cf_listen_addr, parse_addr, NULL); if (!ok) fatal("failed to parse listen_addr list: %s", cf_listen_addr); if (cf_unix_socket_dir && *cf_unix_socket_dir) create_unix_socket(cf_unix_socket_dir, cf_listen_port); if (!statlist_count(&sock_list)) fatal("nowhere to listen on"); resume_pooler(); } bool for_each_pooler_fd(pooler_cb cbfunc, void *arg) { struct List *el; struct ListenSocket *ls; bool ok; statlist_for_each(el, &sock_list) { ls = container_of(el, struct ListenSocket, node); ok = cbfunc(arg, ls->fd, &ls->addr); if (!ok) return false; } return true; } pgbouncer-1.7/src/hba.c0000664000175000017500000003631012630306415011760 00000000000000/* * Host-Based-Access-control file support. * * Copyright (c) 2015 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "bouncer.h" #include #include #include enum RuleType { RULE_LOCAL, RULE_HOST, RULE_HOSTSSL, RULE_HOSTNOSSL, }; #define NAME_ALL 1 #define NAME_SAMEUSER 2 struct NameSlot { size_t strlen; char str[]; }; struct HBAName { unsigned int flags; struct StrSet *name_set; }; struct HBARule { struct List node; enum RuleType rule_type; int rule_method; int rule_af; uint8_t rule_addr[16]; uint8_t rule_mask[16]; struct HBAName db_name; struct HBAName user_name; }; struct HBA { struct List rules; }; /* * StrSet */ struct StrSetNode { unsigned int s_len; char s_val[FLEX_ARRAY]; }; struct StrSet { CxMem *pool; unsigned count; unsigned alloc; struct StrSetNode **nodes; struct CBTree *cbtree; }; struct StrSet *strset_new(CxMem *cx); void strset_free(struct StrSet *set); bool strset_add(struct StrSet *set, const char *str, unsigned int len); bool strset_contains(struct StrSet *set, const char *str, unsigned int len); struct StrSet *strset_new(CxMem *cx) { struct StrSet *set; CxMem *pool; pool = cx_new_pool(cx, 1024, 0); if (!pool) return NULL; set = cx_alloc(pool, sizeof *set); if (!set) return NULL; set->pool = pool; set->cbtree = NULL; set->count = 0; set->alloc = 10; set->nodes = cx_alloc0(pool, set->alloc * sizeof(struct StrSet *)); if (!set->nodes) { cx_destroy(pool); return NULL; } return set; } static size_t strset_node_key(void *ctx, void *obj, const void **ptr_p) { struct StrSetNode *node = obj; *ptr_p = node->s_val; return node->s_len; } bool strset_add(struct StrSet *set, const char *str, unsigned int len) { struct StrSetNode *node; unsigned int i; bool ok; if (strset_contains(set, str, len)) return true; node = cx_alloc(set->pool, offsetof(struct StrSetNode, s_val) + len + 1); if (!node) return false; node->s_len = len; memcpy(node->s_val, str, len); node->s_val[len] = 0; if (set->count < set->alloc) { set->nodes[set->count++] = node; return true; } if (!set->cbtree) { set->cbtree = cbtree_create(strset_node_key, NULL, set, set->pool); if (!set->cbtree) return false; for (i = 0; i < set->count; i++) { ok = cbtree_insert(set->cbtree, set->nodes[i]); if (!ok) return false; } } ok = cbtree_insert(set->cbtree, node); if (!ok) return false; set->count++; return true; } bool strset_contains(struct StrSet *set, const char *str, unsigned int len) { unsigned int i; struct StrSetNode *node; if (set->cbtree) return cbtree_lookup(set->cbtree, str, len) != NULL; for (i = 0; i < set->count; i++) { node = set->nodes[i]; if (node->s_len != len) continue; if (memcmp(node->s_val, str, len) == 0) return true; } return false; } void strset_free(struct StrSet *set) { if (set) cx_destroy(set->pool); } /* * Parse HBA tokens. */ enum TokType { TOK_STRING, TOK_IDENT, TOK_COMMA, TOK_FAIL, TOK_EOL }; struct TokParser { const char *pos; enum TokType cur_tok; char *cur_tok_str; char *buf; size_t buflen; }; static bool tok_buf_check(struct TokParser *p, size_t len) { size_t tmplen; char *tmp; if (p->buflen >= len) return true; tmplen = len*2; tmp = realloc(p->buf, tmplen); if (!tmp) return false; p->buf = tmp; p->buflen = tmplen; return true; } static enum TokType next_token(struct TokParser *p) { const char *s, *s2; char *dst; if (p->cur_tok == TOK_EOL) return TOK_EOL; p->cur_tok_str = NULL; p->cur_tok = TOK_FAIL; while (p->pos[0] && isspace((unsigned char)p->pos[0])) p->pos++; if (p->pos[0] == '#' || p->pos[0] == '\0') { p->cur_tok = TOK_EOL; p->pos = NULL; } else if (p->pos[0] == ',') { p->cur_tok = TOK_COMMA; p->pos++; } else if (p->pos[0] == '"') { for (s = p->pos+1; s[0]; s++) { if (s[0] == '"') { if (s[1] == '"') s++; else break; } } if (s[0] != '"' || !tok_buf_check(p, s - p->pos)) return TOK_FAIL; dst = p->buf; for (s2 = p->pos+1; s2 < s; s2++) { *dst++ = *s2; if (*s2 == '"') s2++; } *dst = 0; p->pos = s + 1; p->cur_tok = TOK_STRING; p->cur_tok_str = p->buf; } else { for (s = p->pos + 1; *s; s++) { if (*s == ',' || *s == '#' || *s == '"') break; if (isspace((unsigned char)*s)) break; } if (!tok_buf_check(p, s - p->pos)) return TOK_FAIL; memcpy(p->buf, p->pos, s - p->pos); p->buf[s - p->pos] = 0; p->pos = s; p->cur_tok = TOK_IDENT; p->cur_tok_str = p->buf; } return p->cur_tok; } static bool eat(struct TokParser *p, enum TokType ttype) { if (p->cur_tok == ttype) { next_token(p); return true; } return false; } static bool eat_kw(struct TokParser *p, const char *kw) { if (p->cur_tok == TOK_IDENT && strcmp(kw, p->cur_tok_str) == 0) { next_token(p); return true; } return false; } static bool expect(struct TokParser *tp, enum TokType ttype, const char **str_p) { if (tp->cur_tok == ttype) { *str_p = tp->buf; return true; } return false; } static char *path_join(const char *p1, const char *p2) { size_t len1, len2; char *res = NULL, *pos; if (p2[0] == '/' || p1[0] == 0 || !memcmp(p1, ".", 2)) return strdup(p2); len1 = strlen(p1); len2 = strlen(p2); res = malloc(len1 + len2 + 2 + 1); if (res) { memcpy(res, p1, len1); pos = res + len1; if (pos[-1] != '/') *pos++ = '/'; memcpy(pos, p2, len2 + 1); } return res; } static char *path_join_dirname(const char *parent, const char *fn) { char *tmp, *res; const char *basedir; if (fn[0] == '/') return strdup(fn); tmp = strdup(parent); if (!tmp) return NULL; basedir = dirname(tmp); res = path_join(basedir, fn); free(tmp); return res; } static void init_parser(struct TokParser *p) { memset(p, 0, sizeof(*p)); } static void parse_from_string(struct TokParser *p, const char *str) { p->pos = str; p->cur_tok = TOK_COMMA; p->cur_tok_str = NULL; next_token(p); } static void free_parser(struct TokParser *p) { free(p->buf); p->buf = NULL; } static bool parse_names(struct HBAName *hname, struct TokParser *p, bool is_db, const char *parent_filename); static bool parse_namefile(struct HBAName *hname, const char *fn, bool is_db) { FILE *f; ssize_t len; char *ln = NULL; size_t buflen = 0; int linenr; bool ok = false; struct TokParser tp; init_parser(&tp); f = fopen(fn, "r"); if (!f) { free(fn); return false; } for (linenr = 1; ; linenr++) { len = getline(&ln, &buflen, f); if (len < 0) { ok = true; break; } parse_from_string(&tp, ln); if (!parse_names(hname, &tp, is_db, fn)) break; } free_parser(&tp); free(fn); free(ln); fclose(f); return ok; } static bool parse_names(struct HBAName *hname, struct TokParser *tp, bool is_db, const char *parent_filename) { const char *tok; while (1) { if (eat_kw(tp, "all")) { hname->flags |= NAME_ALL; goto eat_comma; } if (is_db) { if (eat_kw(tp, "sameuser")) { hname->flags |= NAME_SAMEUSER; goto eat_comma; } if (eat_kw(tp, "samerole")) { return false; } if (eat_kw(tp, "samegroup")) { return false; } if (eat_kw(tp, "replication")) { return false; } } if (expect(tp, TOK_IDENT, &tok)) { if (tok[0] == '+') { return false; } if (tok[0] == '@') { bool ok; const char *fn; fn = path_join_dirname(parent_filename, tok + 1); if (!fn) return false; ok = parse_namefile(hname, fn, is_db); free(fn); if (!ok) return false; goto eat_comma; } /* fallthrough */ } else if (expect(tp, TOK_STRING, &tok)) { /* fallthrough */ } else { return false; } /* * TOK_IDENT or TOK_STRING as plain name. */ if (!hname->name_set) { hname->name_set = strset_new(NULL); if (!hname->name_set) return false; } if (!strset_add(hname->name_set, tok, strlen(tok))) return false; next_token(tp); eat_comma: if (!eat(tp, TOK_COMMA)) break; } return true; } static void rule_free(struct HBARule *rule) { free(rule); } static bool parse_addr(struct HBARule *rule, const char *addr) { if (inet_pton(AF_INET6, addr, rule->rule_addr)) { rule->rule_af = AF_INET6; } else if (inet_pton(AF_INET, addr, rule->rule_addr)) { rule->rule_af = AF_INET; } else { return false; } return true; } static bool parse_nmask(struct HBARule *rule, const char *nmask) { char *end = NULL; unsigned long bits; unsigned int i; errno = 0; bits = strtoul(nmask, &end, 10); if (errno || *end) { return false; } if (rule->rule_af == AF_INET && bits > 32) { return false; } if (rule->rule_af == AF_INET6 && bits > 128) { return false; } for (i = 0; i < bits/8; i++) rule->rule_mask[i] = 255; if (bits % 8) rule->rule_mask[i] = 255 << (8 - (bits % 8)); return true; } static bool bad_mask(struct HBARule *rule) { int i, bytes = rule->rule_af == AF_INET ? 4 : 16; uint8_t res = 0; for (i = 0; i < bytes; i++) res |= rule->rule_addr[i] & (255 ^ rule->rule_mask[i]); return !!res; } static bool parse_line(struct HBA *hba, struct TokParser *tp, int linenr, const char *parent_filename) { const char *addr = NULL, *mask = NULL; enum RuleType rtype; char *nmask = NULL; struct HBARule *rule = NULL; if (eat_kw(tp, "local")) { rtype = RULE_LOCAL; } else if (eat_kw(tp, "host")) { rtype = RULE_HOST; } else if (eat_kw(tp, "hostssl")) { rtype = RULE_HOSTSSL; } else if (eat_kw(tp, "hostnossl")) { rtype = RULE_HOSTNOSSL; } else if (eat(tp, TOK_EOL)) { return true; } else { log_warning("hba line %d: unknown type", linenr); return false; } rule = calloc(sizeof *rule, 1); if (!rule) { log_warning("hba: no mem for rule"); goto failed; } rule->rule_type = rtype; if (!parse_names(&rule->db_name, tp, true, parent_filename)) goto failed; if (!parse_names(&rule->user_name, tp, true, parent_filename)) goto failed; if (rtype == RULE_LOCAL) { rule->rule_af = AF_UNIX; } else { if (!expect(tp, TOK_IDENT, &addr)) { log_warning("hba line %d: did not find address - %d - '%s'", linenr, tp->cur_tok, tp->buf); goto failed; } nmask = strchr(addr, '/'); if (nmask) { *nmask++ = 0; } if (!parse_addr(rule, addr)) { log_warning("hba line %d: failed to parse address - %s", linenr, addr); goto failed; } if (nmask) { if (!parse_nmask(rule, nmask)) { log_warning("hba line %d: invalid mask", linenr); goto failed; } next_token(tp); } else { next_token(tp); if (!expect(tp, TOK_IDENT, &mask)) { log_warning("hba line %d: did not find mask", linenr); goto failed; } if (!inet_pton(rule->rule_af, mask, rule->rule_mask)) { log_warning("hba line %d: failed to parse mask: %s", linenr, mask); goto failed; } next_token(tp); } if (bad_mask(rule)) { char buf1[128], buf2[128]; log_warning("Addres does not match mask in %s line #%d: %s / %s", parent_filename, linenr, inet_ntop(rule->rule_af, rule->rule_addr, buf1, sizeof buf1), inet_ntop(rule->rule_af, rule->rule_mask, buf2, sizeof buf2)); } } if (eat_kw(tp, "trust")) { rule->rule_method = AUTH_TRUST; } else if (eat_kw(tp, "reject")) { rule->rule_method = AUTH_REJECT; } else if (eat_kw(tp, "md5")) { rule->rule_method = AUTH_MD5; } else if (eat_kw(tp, "password")) { rule->rule_method = AUTH_PLAIN; } else if (eat_kw(tp, "peer")) { rule->rule_method = AUTH_PEER; } else if (eat_kw(tp, "cert")) { rule->rule_method = AUTH_CERT; } else { log_warning("hba line %d: unsupported method: buf=%s", linenr, tp->buf); goto failed; } if (!eat(tp, TOK_EOL)) { log_warning("hba line %d: unsupported parameters", linenr); goto failed; } list_append(&hba->rules, &rule->node); return true; failed: rule_free(rule); return false; } struct HBA *hba_load_rules(const char *fn) { struct HBA *hba = NULL; FILE *f = NULL; char *ln = NULL; size_t lnbuf = 0; ssize_t len; int linenr; struct TokParser tp; init_parser(&tp); hba = malloc(sizeof *hba); if (!hba) goto out; list_init(&hba->rules); f = fopen(fn, "r"); if (!f) goto out; for (linenr = 1; ; linenr++) { len = getline(&ln, &lnbuf, f); if (len < 0) break; parse_from_string(&tp, ln); if (!parse_line(hba, &tp, linenr, fn)) { free(hba); hba = NULL; break; } } out: free_parser(&tp); free(ln); if (f) fclose(f); return hba; } void hba_free(struct HBA *hba) { struct List *el, *tmp; struct HBARule *rule; if (!hba) return; list_for_each_safe(el, &hba->rules, tmp) { rule = container_of(el, struct HBARule, node); list_del(&rule->node); rule_free(rule); } free(hba); } static bool name_match(struct HBAName *hname, const char *name, unsigned int namelen, const char *pair) { if (hname->flags & NAME_ALL) return true; if ((hname->flags & NAME_SAMEUSER) && strcmp(name, pair) == 0) return true; if (hname->name_set) return strset_contains(hname->name_set, name, namelen); return false; } static bool match_inet4(const struct HBARule *rule, PgAddr *addr) { const uint32_t *src, *base, *mask; if (pga_family(addr) != AF_INET) return false; src = (uint32_t *)&addr->sin.sin_addr.s_addr; base = (uint32_t *)rule->rule_addr; mask = (uint32_t *)rule->rule_mask; return (src[0] & mask[0]) == base[0]; } static bool match_inet6(const struct HBARule *rule, PgAddr *addr) { const uint32_t *src, *base, *mask; if (pga_family(addr) != AF_INET6) return false; src = (uint32_t *)addr->sin6.sin6_addr.s6_addr; base = (uint32_t *)rule->rule_addr; mask = (uint32_t *)rule->rule_mask; return (src[0] & mask[0]) == base[0] && (src[1] & mask[1]) == base[1] && (src[2] & mask[2]) == base[2] && (src[3] & mask[3]) == base[3]; } int hba_eval(struct HBA *hba, PgAddr *addr, bool is_tls, const char *dbname, const char *username) { struct List *el; struct HBARule *rule; unsigned int dbnamelen = strlen(dbname); unsigned int unamelen = strlen(username); if (!hba) return AUTH_REJECT; list_for_each(el, &hba->rules) { rule = container_of(el, struct HBARule, node); /* match address */ if (pga_is_unix(addr)) { if (rule->rule_type != RULE_LOCAL) continue; } else if (rule->rule_type == RULE_LOCAL) { continue; } else if (rule->rule_type == RULE_HOSTSSL && !is_tls) { continue; } else if (rule->rule_type == RULE_HOSTNOSSL && is_tls) { continue; } else if (rule->rule_af == AF_INET) { if (!match_inet4(rule, addr)) continue; } else if (rule->rule_af == AF_INET6) { if (!match_inet6(rule, addr)) continue; } else { continue; } /* match db & user */ if (!name_match(&rule->db_name, dbname, dbnamelen, username)) continue; if (!name_match(&rule->user_name, username, unamelen, dbname)) continue; /* rule matches */ return rule->rule_method; } return AUTH_REJECT; } pgbouncer-1.7/src/dnslookup.c0000664000175000017500000010571312561345127013256 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2010 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "bouncer.h" #include /* * Available backends: * * c-ares - libcares * udns - libudns * getaddrinfo_a - glibc only * libevent1 - returns TTL, ignores hosts file. * libevent2 - does not return TTL, uses hosts file. */ #if !defined(USE_EVDNS) && !defined(USE_UDNS) && !defined(USE_CARES) #define USE_GETADDRINFO_A #endif #ifdef USE_EVDNS #ifdef EV_ET #define USE_LIBEVENT2 #include #define addrinfo evutil_addrinfo #define freeaddrinfo evutil_freeaddrinfo #else /* !EV_ET */ #define USE_LIBEVENT1 #include #endif /* !EV_ET */ #endif /* USE_EVDNS */ #ifdef USE_CARES #include #include #include #define ZONE_RECHECK 1 #else /* only c-ares requires this */ #define impl_per_loop(ctx) #endif #ifdef USE_UDNS #include #define ZONE_RECHECK 1 #endif #ifndef ZONE_RECHECK #define ZONE_RECHECK 0 /* no implementation, also avoid 'unused' warning */ #define impl_query_soa_serial(ctx, name) do { if (0) got_zone_serial(ctx, NULL); } while (0) #define cf_dns_zone_check_period (0) #endif /* * There can be several client request (tokens) * attached to single actual request. */ struct DNSToken { struct List node; adns_callback_f cb_func; void *cb_arg; }; /* * Cached DNS query (hostname). */ struct DNSRequest { struct AANode node; /* DNSContext->req_tree */ struct List znode; /* DNSZone->host_list */ struct DNSContext *ctx; struct DNSZone *zone; struct List ucb_list; /* DNSToken->node */ const char *name; int namelen; bool done; struct addrinfo *result; struct addrinfo *current; struct addrinfo *oldres; usec_t res_ttl; }; /* zone name serial */ struct DNSZone { struct List lnode; /* DNSContext->zone_list */ struct AANode tnode; /* DNSContext->zone_tree */ struct StatList host_list; /* DNSRequest->znode */ const char *zonename; uint32_t serial; }; /* * Top struct for DNS data. */ struct DNSContext { struct AATree req_tree; void *edns; struct AATree zone_tree; /* DNSZone->tnode */ struct List zone_list; /* DNSZone->lnode */ struct DNSZone *cur_zone; struct event ev_zone_timer; int zone_state; int active; /* number of in-flight queries */ }; static void deliver_info(struct DNSRequest *req); static void got_result_gai(int result, struct addrinfo *res, void *arg); static void zone_register(struct DNSContext *ctx, struct DNSRequest *req); static void zone_init(struct DNSContext *ctx); static void zone_free(struct DNSContext *ctx); static void got_zone_serial(struct DNSContext *ctx, uint32_t *serial); /* * Custom addrinfo generation */ #if defined(USE_LIBEVENT1) || defined(USE_UDNS) || defined(USE_CARES) static struct addrinfo *mk_addrinfo(const void *adr, int af) { struct addrinfo *ai; ai = calloc(1, sizeof(*ai)); if (!ai) return NULL; if (af == AF_INET) { struct sockaddr_in *sa4; sa4 = calloc(1, sizeof(*sa4)); if (!sa4) goto failed; memcpy(&sa4->sin_addr, adr, 4); sa4->sin_family = af; ai->ai_addr = (struct sockaddr *)sa4; ai->ai_addrlen = sizeof(*sa4); } else if (af == AF_INET6) { struct sockaddr_in6 *sa6; sa6 = calloc(1, sizeof(*sa6)); if (!sa6) goto failed; memcpy(&sa6->sin6_addr, adr, sizeof(sa6->sin6_addr)); sa6->sin6_family = af; ai->ai_addr = (struct sockaddr *)sa6; ai->ai_addrlen = sizeof(*sa6); } ai->ai_protocol = IPPROTO_TCP; ai->ai_socktype = SOCK_STREAM; ai->ai_family = af; return ai; failed: free(ai); return NULL; } #define freeaddrinfo(x) local_freeaddrinfo(x) static void freeaddrinfo(struct addrinfo *ai) { struct addrinfo *cur; while (ai) { cur = ai; ai = ai->ai_next; free(cur->ai_addr); free(cur); } } static inline struct addrinfo *convert_ipv4_result(const struct in_addr *adrs, int count) { struct addrinfo *ai, *first = NULL, *last = NULL; int i; for (i = 0; i < count; i++) { ai = mk_addrinfo(&adrs[i], AF_INET); if (!ai) goto failed; if (!first) first = ai; else last->ai_next = ai; last = ai; } return first; failed: freeaddrinfo(first); return NULL; } static inline struct addrinfo *convert_hostent(const struct hostent *h) { struct addrinfo *ai, *first = NULL, *last = NULL; int i; for (i = 0; h->h_addr_list[i]; i++) { ai = mk_addrinfo(h->h_addr_list[i], h->h_addrtype); if (!ai) goto failed; if (!first) first = ai; else last->ai_next = ai; last = ai; } return first; failed: freeaddrinfo(first); return NULL; } #endif /* custom addrinfo */ /* * ADNS with glibc's getaddrinfo_a() */ #ifdef USE_GETADDRINFO_A const char *adns_get_backend(void) { #ifdef HAVE_GETADDRINFO_A return "libc" #ifdef __GLIBC__ "-" STR(__GLIBC__) "." STR(__GLIBC_MINOR__); #endif ; #else return "compat"; #endif } struct GaiRequest { struct List node; struct DNSRequest *req; struct gaicb gairq; }; struct GaiContext { struct DNSContext *ctx; struct List gairq_list; struct event ev; struct sigevent sev; }; static void dns_signal(int f, short ev, void *arg) { struct GaiContext *gctx = arg; struct List *el, *tmp; struct GaiRequest *rq; int e; list_for_each_safe(el, &gctx->gairq_list, tmp) { rq = container_of(el, struct GaiRequest, node); e = gai_error(&rq->gairq); if (e == EAI_INPROGRESS) continue; /* got one */ list_del(&rq->node); got_result_gai(e, rq->gairq.ar_result, rq->req); free(rq); } } static bool impl_init(struct DNSContext *ctx) { struct GaiContext *gctx = calloc(1, sizeof(*gctx)); if (!gctx) return false; list_init(&gctx->gairq_list); gctx->ctx = ctx; gctx->sev.sigev_notify = SIGEV_SIGNAL; gctx->sev.sigev_signo = SIGALRM; signal_set(&gctx->ev, SIGALRM, dns_signal, gctx); if (signal_add(&gctx->ev, NULL) < 0) { free(gctx); return false; } ctx->edns = gctx; return true; } static void impl_launch_query(struct DNSRequest *req) { static const struct addrinfo hints = { .ai_socktype = SOCK_STREAM }; struct GaiContext *gctx = req->ctx->edns; struct GaiRequest *grq; int res; struct gaicb *cb; grq = calloc(1, sizeof(*grq)); if (!grq) goto failed2; list_init(&grq->node); grq->req = req; grq->gairq.ar_name = req->name; grq->gairq.ar_request = &hints; list_append(&gctx->gairq_list, &grq->node); cb = &grq->gairq; res = getaddrinfo_a(GAI_NOWAIT, &cb, 1, &gctx->sev); if (res != 0) goto failed; return; failed: if (res == EAI_SYSTEM) { log_warning("dns: getaddrinfo_a(%s)=%d, errno=%d (%s)", req->name, res, errno, strerror(errno)); } else { log_warning("dns: getaddrinfo_a(%s)=%d", req->name, res); } list_del(&grq->node); free(grq); failed2: req->done = true; deliver_info(req); } static void impl_release(struct DNSContext *ctx) { struct GaiContext *gctx = ctx->edns; if (gctx) { signal_del(&gctx->ev); free(gctx); ctx->edns = NULL; } } #endif /* USE_GETADDRINFO_A */ /* * ADNS with libevent2 */ #ifdef USE_LIBEVENT2 const char *adns_get_backend(void) { return "evdns2"; } static bool impl_init(struct DNSContext *ctx) { ctx->edns = evdns_base_new(NULL, 1); if (!ctx->edns) { log_warning("evdns_base_new failed"); return false; } return true; } static void impl_launch_query(struct DNSRequest *req) { static const struct addrinfo hints = { .ai_socktype = SOCK_STREAM }; struct evdns_getaddrinfo_request *gai_req; struct evdns_base *dns = req->ctx->edns; gai_req = evdns_getaddrinfo(dns, req->name, NULL, &hints, got_result_gai, req); log_noise("dns: evdns_getaddrinfo(%s)=%p", req->name, gai_req); } static void impl_release(struct DNSContext *ctx) { struct evdns_base *dns = ctx->edns; evdns_base_free(dns, 0); } #endif /* USE_LIBEVENT2 */ /* * ADNS with libevent 1.x */ #ifdef USE_LIBEVENT1 const char *adns_get_backend(void) { return "evdns1"; } static void got_result_evdns(int result, char type, int count, int ttl, void *addresses, void *arg) { struct DNSRequest *req = arg; struct addrinfo *ai; log_noise("dns: got_result_evdns: type=%d cnt=%d ttl=%d", type, count, ttl); if (result == DNS_IPv4_A) { ai = convert_ipv4_result(addresses, count); if (ai) { got_result_gai(0, ai, req); return; } } /* lookup failed */ got_result_gai(1, NULL, req); } static bool impl_init(struct DNSContext *ctx) { return evdns_init() == 0; } static void impl_launch_query(struct DNSRequest *req) { int err; err = evdns_resolve_ipv4(req->name, 0, got_result_evdns, req); log_noise("dns(%s): evdns_resolve_ipv4 = %d", req->name, err); if (err != 0 && !req->done) { /* if callback was not yet called, do it now */ got_result_gai(1, NULL, req); } } static void impl_release(struct DNSContext *ctx) { evdns_shutdown(0); } #endif /* USE_LIBEVENT1 */ /* * ADNS with */ #ifdef USE_UDNS struct UdnsMeta { struct dns_ctx *ctx; struct event ev_io; struct event ev_timer; bool timer_active; }; const char *adns_get_backend(void) { return "udns " UDNS_VERSION; } static void udns_timer_setter(struct dns_ctx *uctx, int timeout, void *arg) { struct DNSContext *ctx = arg; struct UdnsMeta *udns = ctx->edns; log_noise("udns_timer_setter: ctx=%p timeout=%d", uctx, timeout); if (udns->timer_active) { event_del(&udns->ev_timer); udns->timer_active = false; } if (uctx && timeout >= 0) { struct timeval tv = { .tv_sec = timeout, .tv_usec = 0 }; evtimer_add(&udns->ev_timer, &tv); udns->timer_active = true; } } static void udns_timer_cb(int d, short fl, void *arg) { struct DNSContext *ctx = arg; struct UdnsMeta *udns = ctx->edns; time_t now = get_cached_time() / USEC; log_noise("udns_timer_cb"); dns_timeouts(udns->ctx, 10, now); } static void udns_io_cb(int fd, short fl, void *arg) { struct DNSContext *ctx = arg; struct UdnsMeta *udns = ctx->edns; time_t now = get_cached_time() / USEC; log_noise("udns_io_cb"); dns_ioevent(udns->ctx, now); } static void udns_result_a4(struct dns_ctx *ctx, struct dns_rr_a4 *a4, void *data) { struct DNSRequest *req = data; struct addrinfo *res = NULL; int err; err = dns_status(ctx); if (err < 0) { log_warning("udns_result_a4: %s: query failed [%d]", req->name, err); } else if (a4) { log_noise("udns_result_a4: %s: %d ips", req->name, a4->dnsa4_nrr); res = convert_ipv4_result(a4->dnsa4_addr, a4->dnsa4_nrr); free(a4); } else { log_warning("udns_result_a4: %s: missing result", req->name); } got_result_gai(0, res, req); } static void impl_launch_query(struct DNSRequest *req) { struct UdnsMeta *udns = req->ctx->edns; struct dns_query *q; int flags = 0; q = dns_submit_a4(udns->ctx, req->name, flags, udns_result_a4, req); if (q) { log_noise("dns: udns_launch_query(%s)=%p", req->name, q); } else { log_warning("dns: udns_launch_query(%s)=NULL", req->name); } } static bool impl_init(struct DNSContext *ctx) { int fd; struct dns_ctx *dctx; struct UdnsMeta *udns; int err; dns_init(NULL, 0); dctx = dns_new(NULL); if (!dctx) return false; udns = calloc(1, sizeof(*udns)); if (!udns) return false; ctx->edns = udns; udns->ctx = dctx; /* i/o callback setup */ fd = dns_open(dctx); if (fd <= 0) { log_warning("dns_open failed: fd=%d", fd); return false; } event_set(&udns->ev_io, fd, EV_READ | EV_PERSIST, udns_io_cb, ctx); err = event_add(&udns->ev_io, NULL); if (err < 0) log_warning("impl_init: event_add failed: %s", strerror(errno)); /* timer setup */ evtimer_set(&udns->ev_timer, udns_timer_cb, ctx); dns_set_tmcbck(udns->ctx, udns_timer_setter, ctx); return true; } static void impl_release(struct DNSContext *ctx) { struct UdnsMeta *udns = ctx->edns; event_del(&udns->ev_io); dns_free(udns->ctx); if (udns->timer_active) { event_del(&udns->ev_timer); udns->timer_active = false; } } /* * generic SOA query for UDNS */ struct SOA { dns_rr_common(dnssoa); char *dnssoa_nsname; char *dnssoa_hostmaster; uint32_t dnssoa_serial; uint32_t dnssoa_refresh; uint32_t dnssoa_retry; uint32_t dnssoa_expire; uint32_t dnssoa_minttl; }; typedef void query_soa_fn(struct dns_ctx *ctx, struct SOA *result, void *data); static int parse_soa(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end, void **result) { struct SOA *soa = NULL; int res, len; struct dns_parse p; struct dns_rr rr; dnsc_t buf[DNS_MAXDN]; char *s; /* calc size */ len = 0; dns_initparse(&p, qdn, pkt, cur, end); while ((res = dns_nextrr(&p, &rr)) > 0) { cur = rr.dnsrr_dptr; res = dns_getdn(pkt, &cur, end, buf, sizeof(buf)); if (res <= 0) goto failed; len += dns_dntop_size(buf); res = dns_getdn(pkt, &cur, end, buf, sizeof(buf)); if (res <= 0) goto failed; len += dns_dntop_size(buf); if (cur + 5*4 != rr.dnsrr_dend) goto failed; } if (res < 0 || p.dnsp_nrr != 1) goto failed; len += dns_stdrr_size(&p); /* allocate */ soa = malloc(sizeof(*soa) + len); if (!soa) return DNS_E_NOMEM; /* fill with data */ soa->dnssoa_nrr = 1; dns_rewind(&p, qdn); dns_nextrr(&p, &rr); s = (char *)(soa + 1); cur = rr.dnsrr_dptr; soa->dnssoa_nsname = s; dns_getdn(pkt, &cur, end, buf, sizeof(buf)); s += dns_dntop(buf, s, DNS_MAXNAME); soa->dnssoa_hostmaster = s; dns_getdn(pkt, &cur, end, buf, sizeof(buf)); s += dns_dntop(buf, s, DNS_MAXNAME); soa->dnssoa_serial = dns_get32(cur + 0*4); soa->dnssoa_refresh = dns_get32(cur + 1*4); soa->dnssoa_retry = dns_get32(cur + 2*4); soa->dnssoa_expire = dns_get32(cur + 3*4); soa->dnssoa_minttl = dns_get32(cur + 4*4); dns_stdrr_finish((struct dns_rr_null *)soa, s, &p); *result = soa; return 0; failed: free(soa); return DNS_E_PROTOCOL; } static struct dns_query * submit_soa(struct dns_ctx *ctx, const char *name, int flags, query_soa_fn *cb, void *data) { return dns_submit_p(ctx, name, DNS_C_IN, DNS_T_SOA, flags, parse_soa, (dns_query_fn *)cb, data); } /* * actual "get serial" part */ static void udns_result_soa(struct dns_ctx *uctx, struct SOA *soa, void *data) { struct DNSContext *ctx = data; if (!soa) { log_noise("SOA query failed"); got_zone_serial(ctx, NULL); return; } log_noise("SOA1: cname=%s qname=%s ttl=%u nrr=%u", soa->dnssoa_cname, soa->dnssoa_qname, soa->dnssoa_ttl, soa->dnssoa_nrr); log_noise("SOA2: nsname=%s hostmaster=%s serial=%u refresh=%u retry=%u expire=%u minttl=%u", soa->dnssoa_nsname, soa->dnssoa_hostmaster, soa->dnssoa_serial, soa->dnssoa_refresh, soa->dnssoa_retry, soa->dnssoa_expire, soa->dnssoa_minttl); got_zone_serial(ctx, &soa->dnssoa_serial); free(soa); } static int impl_query_soa_serial(struct DNSContext *ctx, const char *zonename) { struct UdnsMeta *udns = ctx->edns; struct dns_query *q; int flags = 0; log_debug("udns: impl_query_soa_serial: name=%s", zonename); q = submit_soa(udns->ctx, zonename, flags, udns_result_soa, ctx); if (!q) { log_error("impl_query_soa_serial failed: %s", zonename); } return 0; } #endif /* USE_UDNS */ /* * ADNS with */ #ifdef USE_CARES #define MAX_CARES_FDS 16 struct XaresFD { struct event ev; /* fd event state is persistent */ struct XaresMeta *meta; /* pointer to parent context */ ares_socket_t sock; /* socket value */ short wait; /* EV_READ / EV_WRITE */ bool in_use; /* is this slot assigned */ }; struct XaresMeta { /* c-ares descriptor */ ares_channel chan; /* how many elements in fds array are in use */ int max_fds; /* static array for fds */ struct XaresFD fds[MAX_CARES_FDS]; /* timer event is one-shot */ struct event ev_timer; /* is timer activated? */ bool timer_active; /* If dns events happened during event loop, timer may need recalibration. */ int got_events; }; const char *adns_get_backend(void) { return "c-ares " ARES_VERSION_STR; } /* called by libevent on timer timeout */ static void xares_timer_cb(int sock, short flags, void *arg) { struct DNSContext *ctx = arg; struct XaresMeta *meta = ctx->edns; ares_process_fd(meta->chan, ARES_SOCKET_BAD, ARES_SOCKET_BAD); meta->timer_active = 0; meta->got_events = 1; } /* called by libevent on fd event */ static void xares_fd_cb(int sock, short flags, void *arg) { struct XaresFD *xfd = arg; struct XaresMeta *meta = xfd->meta; ares_socket_t r, w; r = (flags & EV_READ) ? xfd->sock : ARES_SOCKET_BAD; w = (flags & EV_WRITE) ? xfd->sock : ARES_SOCKET_BAD; ares_process_fd(meta->chan, r, w); meta->got_events = 1; } /* called by c-ares on new socket creation */ static int xares_new_socket_cb(ares_socket_t sock, int sock_type, void *arg) { struct DNSContext *ctx = arg; struct XaresMeta *meta = ctx->edns; struct XaresFD *xfd; int pos; /* find free slot in array */ for (pos = 0; pos < meta->max_fds; pos++) { if (!meta->fds[pos].in_use) break; } if (pos >= MAX_CARES_FDS) { log_warning("c-ares fd overflow"); return ARES_ENOMEM; } if (pos == meta->max_fds) meta->max_fds++; /* fill it */ xfd = &meta->fds[pos]; xfd->meta = meta; xfd->sock = sock; xfd->wait = 0; xfd->in_use = 1; return ARES_SUCCESS; } /* called by c-ares on socket state change (r=w=0 means socket close) */ static void xares_state_cb(void *arg, ares_socket_t sock, int r, int w) { struct DNSContext *ctx = arg; struct XaresMeta *meta = ctx->edns; struct XaresFD *xfd; int pos; short new_wait = 0; if (r) new_wait |= EV_READ; if (w) new_wait |= EV_WRITE; /* find socket */ for (pos = 0; pos < meta->max_fds; pos++) { xfd = &meta->fds[pos]; if (!xfd->in_use) continue; if (xfd->sock != sock) continue; /* no change? */ if (xfd->wait == new_wait) return; goto re_set; } log_warning("adns: c-ares state change for unknown fd: %u", (unsigned)sock); return; re_set: if (xfd->wait) event_del(&xfd->ev); xfd->wait = new_wait; if (new_wait) { event_set(&xfd->ev, sock, new_wait | EV_PERSIST, xares_fd_cb, xfd); if (event_add(&xfd->ev, NULL) < 0) log_warning("adns: event_add failed: %s", strerror(errno)); } else { xfd->in_use = 0; } return; } /* called by c-ares on dns reply */ static void xares_host_cb(void *arg, int status, int timeouts, struct hostent *h) { struct DNSRequest *req = arg; struct addrinfo *res = NULL; log_noise("dns: xares_host_cb(%s)=%s", req->name, ares_strerror(status)); if (status == ARES_SUCCESS) { res = convert_hostent(h); got_result_gai(0, res, req); } else { log_debug("DNS lookup failed: %s - %s", req->name, ares_strerror(status)); got_result_gai(0, res, req); } } /* send hostname query */ static void impl_launch_query(struct DNSRequest *req) { struct XaresMeta *meta = req->ctx->edns; int af; /* * c-ares <= 1.10 cannot resolve CNAME with AF_UNSPEC. * * Force IPv4 there. * * Fixed in "host_callback: Fall back to AF_INET on searching with AF_UNSPEC" (c1fe47f) * in c-ares repo. */ #if ARES_VERSION <= 0x10A00 #warning Forcing c-ares to be IPv4-only. af = AF_INET; #else af = AF_UNSPEC; #endif log_noise("dns: ares_gethostbyname(%s)", req->name); ares_gethostbyname(meta->chan, req->name, af, xares_host_cb, req); meta->got_events = 1; } /* re-set timer if any dns event happened */ static void impl_per_loop(struct DNSContext *ctx) { struct timeval tv, *tvp; struct XaresMeta *meta = ctx->edns; if (!meta->got_events) return; if (meta->timer_active) { event_del(&meta->ev_timer); meta->timer_active = false; } tvp = ares_timeout(meta->chan, NULL, &tv); if (tvp != NULL) { if (event_add(&meta->ev_timer, tvp) < 0) log_warning("impl_per_loop: event_add failed: %s", strerror(errno)); meta->timer_active = true; } meta->got_events = 0; } /* c-ares setup */ static bool impl_init(struct DNSContext *ctx) { struct XaresMeta *meta; int err; int mask; struct ares_options opts; err = ares_library_init(ARES_LIB_INIT_ALL); if (err) { log_error("ares_library_init: %s", ares_strerror(err)); return false; } meta = calloc(1, sizeof(*meta)); if (!meta) return false; memset(&opts, 0, sizeof(opts)); opts.sock_state_cb = xares_state_cb; opts.sock_state_cb_data = ctx; mask = ARES_OPT_SOCK_STATE_CB; err = ares_init_options(&meta->chan, &opts, mask); if (err) { free(meta); log_error("ares_library_init: %s", ares_strerror(err)); return false; } ares_set_socket_callback(meta->chan, xares_new_socket_cb, ctx); evtimer_set(&meta->ev_timer, xares_timer_cb, ctx); ctx->edns = meta; return true; } /* c-ares shutdown */ static void impl_release(struct DNSContext *ctx) { struct XaresMeta *meta = ctx->edns; ares_destroy(meta->chan); ares_library_cleanup(); if (meta->timer_active) event_del(&meta->ev_timer); free(meta); ctx->edns = NULL; } /* * query SOA with c-ares */ #ifndef HAVE_ARES_PARSE_SOA_REPLY #define ares_soa_reply xares_soa_reply #define ares_parse_soa_reply xares_parse_soa_reply struct ares_soa_reply { char *nsname; char *hostmaster; uint32_t serial; uint32_t refresh; uint32_t retry; uint32_t expire; uint32_t minttl; }; static void xares_free_soa(struct ares_soa_reply *soa) { if (soa) { if (soa->nsname) free(soa->nsname); if (soa->hostmaster) free(soa->hostmaster); free(soa); } } /* * Full SOA reply packet structure (rfc1035) * * 1) header * id:16, flags:16, qdcount:16, ancount:16, nscount:16, arcount:16 * * 2) query (qdcount) * qname:name, qtype:16, qclass:16 * * 3) answer (ancount) * name:name, type:16, class:16, ttl:32, rdlength:16 * * 3.1) soa rdata * nsname:name, hostmaster:name, * serial:32, refresh:32, retry:32, expire:32, minimum:32 * * 4) authority (nscount) - ignored * * 5) additional (arcount) - ignored */ static int ares_parse_soa_reply(const unsigned char *abuf, int alen, struct ares_soa_reply **soa_p) { const unsigned char *aptr; long len; char *qname = NULL, *rr_name = NULL; struct ares_soa_reply *soa = NULL; int qdcount, ancount; int status; if (alen < NS_HFIXEDSZ) return ARES_EBADRESP; /* parse message header */ qdcount = DNS_HEADER_QDCOUNT(abuf); ancount = DNS_HEADER_ANCOUNT(abuf); if (qdcount != 1 || ancount != 1) return ARES_EBADRESP; aptr = abuf + NS_HFIXEDSZ; /* allocate result struct */ soa = calloc(1, sizeof(*soa)); if (!soa) return ARES_ENOMEM; /* parse query */ status = ares_expand_name(aptr, abuf, alen, &qname, &len); if (status != ARES_SUCCESS) goto failed_stat; aptr += len; /* skip qtype & qclass */ if (aptr + NS_QFIXEDSZ > abuf + alen) goto failed; aptr += NS_QFIXEDSZ; /* parse RR header */ status = ares_expand_name(aptr, abuf, alen, &rr_name, &len); if (status != ARES_SUCCESS) goto failed_stat; aptr += len; /* skip rr_type, rr_class, rr_ttl, rr_rdlen */ if (aptr + NS_RRFIXEDSZ > abuf + alen) goto failed; aptr += NS_RRFIXEDSZ; /* nsname */ status = ares_expand_name(aptr, abuf, alen, &soa->nsname, &len); if (status != ARES_SUCCESS) goto failed_stat; aptr += len; /* hostmaster */ status = ares_expand_name(aptr, abuf, alen, &soa->hostmaster, &len); if (status != ARES_SUCCESS) goto failed_stat; aptr += len; /* integer fields */ if (aptr + 5*4 > abuf + alen) goto failed; soa->serial = DNS__32BIT(aptr + 0*4); soa->refresh = DNS__32BIT(aptr + 1*4); soa->retry = DNS__32BIT(aptr + 2*4); soa->expire = DNS__32BIT(aptr + 3*4); soa->minttl = DNS__32BIT(aptr + 4*4); log_noise("Ares SOA result: qname=%s rr_name=%s serial=%u", qname, rr_name, soa->serial); free(qname); free(rr_name); *soa_p = soa; return ARES_SUCCESS; failed: status = ARES_EBADRESP; failed_stat: xares_free_soa(soa); if (qname) free(qname); if (rr_name) free(rr_name); return (status == ARES_EBADNAME) ? ARES_EBADRESP : status; } #else /* HAVE_ARES_PARSE_SOA_REPLY */ static void xares_free_soa(struct ares_soa_reply *soa) { ares_free_data(soa); } #endif /* HAVE_ARES_PARSE_SOA_REPLY */ /* called by c-ares on SOA reply */ static void xares_soa_cb(void *arg, int status, int timeouts, unsigned char *abuf, int alen) { struct DNSContext *ctx = arg; struct XaresMeta *meta = ctx->edns; struct ares_soa_reply *soa = NULL; meta->got_events = 1; log_noise("ares SOA result: %s", ares_strerror(status)); if (status != ARES_SUCCESS) { got_zone_serial(ctx, NULL); return; } status = ares_parse_soa_reply(abuf, alen, &soa); if (status == ARES_SUCCESS) { got_zone_serial(ctx, &soa->serial); } else { log_warning("ares_parse_soa: %s", ares_strerror(status)); got_zone_serial(ctx, NULL); } xares_free_soa(soa); } /* send SOA query */ static int impl_query_soa_serial(struct DNSContext *ctx, const char *zonename) { struct XaresMeta *meta = ctx->edns; log_debug("dns: ares query SOA(%s)", zonename); ares_search(meta->chan, zonename, ns_c_in, ns_t_soa, xares_soa_cb, ctx); meta->got_events = 1; return 0; } #endif /* USE_CARES */ /* * Generic framework */ static void deliver_info(struct DNSRequest *req) { struct DNSContext *ctx = req->ctx; struct DNSToken *ucb; struct List *el; const struct addrinfo *ai = req->current; char sabuf[128]; ctx->active--; loop: /* get next req */ el = list_pop(&req->ucb_list); if (!el) return; ucb = container_of(el, struct DNSToken, node); /* launch callback */ log_noise("dns: deliver_info(%s) addr=%s", req->name, ai ? sa2str(ai->ai_addr, sabuf, sizeof(sabuf)) : "NULL"); ucb->cb_func(ucb->cb_arg, ai ? ai->ai_addr : NULL, ai ? ai->ai_addrlen : 0); free(ucb); /* scroll req list */ if (ai) { req->current = ai->ai_next; if (!req->current) req->current = req->result; } goto loop; } static int req_cmp(uintptr_t arg, struct AANode *node) { const char *s1 = (char *)arg; struct DNSRequest *req = container_of(node, struct DNSRequest, node); return strcmp(s1, req->name); } static void req_reset(struct DNSRequest *req) { req->done = false; if (req->result) { if (req->oldres) freeaddrinfo(req->oldres); req->oldres = req->result; } req->result = req->current = NULL; } static void req_free(struct AANode *node, void *arg) { struct DNSToken *ucb; struct DNSRequest *req; struct List *el; req = container_of(node, struct DNSRequest, node); while ((el = list_pop(&req->ucb_list)) != NULL) { ucb = container_of(el, struct DNSToken, node); free(ucb); } req_reset(req); if (req->oldres) { freeaddrinfo(req->oldres); req->oldres = NULL; } if (req->zone) statlist_remove(&req->zone->host_list, &req->znode); free(req->name); free(req); } struct DNSContext *adns_create_context(void) { struct DNSContext *ctx; log_debug("adns_create_context: %s", adns_get_backend()); ctx = calloc(1, sizeof(*ctx)); if (!ctx) return NULL; aatree_init(&ctx->req_tree, req_cmp, req_free); zone_init(ctx); if (!impl_init(ctx)) { adns_free_context(ctx); return NULL; } return ctx; } void adns_free_context(struct DNSContext *ctx) { if (ctx) { impl_release(ctx); aatree_destroy(&ctx->req_tree); zone_free(ctx); free(ctx); } } struct DNSToken *adns_resolve(struct DNSContext *ctx, const char *name, adns_callback_f cb_func, void *cb_arg) { int namelen = strlen(name); struct DNSRequest *req; struct DNSToken *ucb; struct AANode *node; /* setup actual lookup */ node = aatree_search(&ctx->req_tree, (uintptr_t)name); if (node) { req = container_of(node, struct DNSRequest, node); } else { log_noise("dns: new req: %s", name); req = calloc(1, sizeof(*req)); if (!req) goto nomem; req->name = strdup(name); if (!req->name) { free(req); goto nomem; } req->ctx = ctx; req->namelen = namelen; list_init(&req->ucb_list); list_init(&req->znode); aatree_insert(&ctx->req_tree, (uintptr_t)req->name, &req->node); zone_register(ctx, req); ctx->active++; impl_launch_query(req); } /* remember user callback */ ucb = calloc(1, sizeof(*ucb)); if (!ucb) goto nomem; list_init(&ucb->node); ucb->cb_func = cb_func; ucb->cb_arg = cb_arg; list_append(&req->ucb_list, &ucb->node); /* if already have final result, report it */ if (req->done) { if (req->res_ttl < get_cached_time()) { log_noise("dns: ttl over: %s", req->name); req_reset(req); ctx->active++; impl_launch_query(req); } else { deliver_info(req); } } /* if ->done, then we have already reported */ return req->done ? NULL : ucb; nomem: log_warning("dns(%s): req failed, no mem", name); cb_func(cb_arg, NULL, 0); return NULL; } static int cmp_addrinfo(const struct addrinfo *a1, const struct addrinfo *a2) { if (a1->ai_family != a2->ai_family) return a1->ai_family - a2->ai_family; if (a1->ai_addrlen != a2->ai_addrlen) return a1->ai_addrlen - a2->ai_addrlen; return memcmp(a1->ai_addr, a2->ai_addr, a1->ai_addrlen); } /* check if new dns reply is missing some IP compared to old one */ static void check_req_result_changes(struct DNSRequest *req) { struct addrinfo *ai, *aj; for (ai = req->oldres; ai; ai = ai->ai_next) { bool found = false; for (aj = req->result; aj; aj = aj->ai_next) { if (cmp_addrinfo(ai, aj) == 0) { found = true; break; } } /* missing IP (possible DNS failover) make connections to it dirty */ if (!found) tag_host_addr_dirty(req->name, ai->ai_addr); } } /* struct addrinfo -> deliver_info() */ static void got_result_gai(int result, struct addrinfo *res, void *arg) { struct DNSRequest *req = arg; req_reset(req); if (result == 0 && res) { req->result = res; req->current = res; if (req->oldres) check_req_result_changes(req); /* show all results */ if (cf_verbose > 1) { const struct addrinfo *ai = res; int n = 0; char buf[128]; while (ai) { log_noise("DNS: %s[%d] = %s [%s]", req->name, n++, sa2str(ai->ai_addr, buf, sizeof(buf)), ai->ai_socktype == SOCK_STREAM ? "STREAM" : "OTHER"); ai = ai->ai_next; } } req->res_ttl = get_cached_time() + cf_dns_max_ttl; } else { /* lookup failed */ log_warning("lookup failed: %s: result=%d", req->name, result); req->res_ttl = get_cached_time() + cf_dns_nxdomain_ttl; } req->done = true; deliver_info(req); } void adns_cancel(struct DNSContext *ctx, struct DNSToken *tk) { list_del(&tk->node); memset(tk, 0, sizeof(*tk)); free(tk); } void adns_info(struct DNSContext *ctx, int *names, int *zones, int *queries, int *pending) { *names = ctx->req_tree.count; *zones = ctx->zone_tree.count; *queries = ctx->active; *pending = 0; } /* * zone code */ static void zone_item_free(struct AANode *n, void *arg) { struct DNSZone *z = container_of(n, struct DNSZone, tnode); list_del(&z->lnode); free(z->zonename); free(z); } static int zone_item_cmp(uintptr_t val1, struct AANode *n2) { const char *name1 = (const char *)val1; struct DNSZone *z2 = container_of(n2, struct DNSZone, tnode); return strcasecmp(name1, z2->zonename); } static void zone_init(struct DNSContext *ctx) { aatree_init(&ctx->zone_tree, zone_item_cmp, zone_item_free); list_init(&ctx->zone_list); } static void zone_free(struct DNSContext *ctx) { aatree_destroy(&ctx->zone_tree); } static void zone_register(struct DNSContext *ctx, struct DNSRequest *req) { struct DNSZone *z; struct AANode *n; const char *name; log_debug("zone_register(%s)", req->name); name = strchr(req->name, '.'); if (!name || name[1] == 0) return; name++; log_debug("zone_register(%s): name=%s", req->name, name); n = aatree_search(&ctx->zone_tree, (uintptr_t)name); if (n) { /* already exists */ z = container_of(n, struct DNSZone, tnode); req->zone = z; statlist_append(&z->host_list, &req->znode); return; } /* create struct */ z = calloc(1, sizeof(*z)); if (!z) return; z->zonename = strdup(name); if (!z->zonename) { free(z); return; } statlist_init(&z->host_list, "host_list"); list_init(&z->lnode); /* link */ aatree_insert(&ctx->zone_tree, (uintptr_t)z->zonename, &z->tnode); list_append(&ctx->zone_list, &z->lnode); statlist_append(&z->host_list, &req->znode); req->zone = z; } static void zone_timer(int fd, short flg, void *arg) { struct DNSContext *ctx = arg; struct List *el; struct DNSZone *z; if (list_empty(&ctx->zone_list)) { ctx->zone_state = 0; return; } el = list_first(&ctx->zone_list); z = container_of(el, struct DNSZone, lnode); ctx->zone_state = 1; ctx->cur_zone = z; ctx->active++; impl_query_soa_serial(ctx, z->zonename); } static void launch_zone_timer(struct DNSContext *ctx) { struct timeval tv; tv.tv_sec = cf_dns_zone_check_period / USEC; tv.tv_usec = cf_dns_zone_check_period % USEC; evtimer_set(&ctx->ev_zone_timer, zone_timer, ctx); safe_evtimer_add(&ctx->ev_zone_timer, &tv); ctx->zone_state = 2; } void adns_zone_cache_maint(struct DNSContext *ctx) { if (!cf_dns_zone_check_period) { if (ctx->zone_state == 2) { event_del(&ctx->ev_zone_timer); ctx->zone_state = 0; } ctx->cur_zone = NULL; return; } else if (ctx->zone_state == 0) { if (list_empty(&ctx->zone_list)) return; launch_zone_timer(ctx); } } static void zone_requeue(struct DNSContext *ctx, struct DNSZone *z) { struct List *el; struct DNSRequest *req; statlist_for_each(el, &z->host_list) { req = container_of(el, struct DNSRequest, znode); if (!req->done) continue; req->res_ttl = 0; ctx->active++; impl_launch_query(req); } } static void got_zone_serial(struct DNSContext *ctx, uint32_t *serial) { struct DNSZone *z = ctx->cur_zone; struct List *el; ctx->active--; if (!ctx->zone_state || !z) return; if (serial) { /* wraparound compare */ int32_t s1 = z->serial; int32_t s2 = *serial; int32_t ds = s2 - s1; if (ds > 0) { log_info("zone '%s' serial changed: old=%u new=%u", z->zonename, z->serial, *serial); z->serial = *serial; zone_requeue(ctx, z); } else { log_debug("zone '%s' unchanged: serial=%u", z->zonename, *serial); } } else { log_debug("failure to get zone '%s' serial", z->zonename); } el = z->lnode.next; if (el != &ctx->zone_list) { z = container_of(el, struct DNSZone, lnode); ctx->cur_zone = z; ctx->active++; impl_query_soa_serial(ctx, z->zonename); } else { launch_zone_timer(ctx); } } /* * Cache walkers */ struct WalkInfo { adns_walk_name_f name_cb; adns_walk_zone_f zone_cb; void *arg; }; static void walk_name(struct AANode *n, void *arg) { struct WalkInfo *w = arg; struct DNSRequest *req = container_of(n, struct DNSRequest, node); w->name_cb(w->arg, req->name, req->result, req->res_ttl); } static void walk_zone(struct AANode *n, void *arg) { struct WalkInfo *w = arg; struct DNSZone *z = container_of(n, struct DNSZone, tnode); w->zone_cb(w->arg, z->zonename, z->serial, statlist_count(&z->host_list)); } void adns_walk_names(struct DNSContext *ctx, adns_walk_name_f cb, void *arg) { struct WalkInfo w; w.name_cb = cb; w.arg = arg; aatree_walk(&ctx->req_tree, AA_WALK_IN_ORDER, walk_name, &w); } void adns_walk_zones(struct DNSContext *ctx, adns_walk_zone_f cb, void *arg) { struct WalkInfo w; w.zone_cb = cb; w.arg = arg; aatree_walk(&ctx->zone_tree, AA_WALK_IN_ORDER, walk_zone, &w); } void adns_per_loop(struct DNSContext *ctx) { impl_per_loop(ctx); } pgbouncer-1.7/src/proto.c0000664000175000017500000002234412572127367012407 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Pieces that need to have detailed info about protocol. */ #include "bouncer.h" /* * parse protocol header from struct MBuf */ /* parses pkt header from buffer, returns false if failed */ bool get_header(struct MBuf *data, PktHdr *pkt) { unsigned type; uint32_t len; unsigned got; unsigned avail; uint16_t len16; uint8_t type8; uint32_t code; struct MBuf hdr; const uint8_t *ptr; mbuf_copy(data, &hdr); if (mbuf_avail_for_read(&hdr) < NEW_HEADER_LEN) { log_noise("get_header: less than 5 bytes available"); return false; } if (!mbuf_get_byte(&hdr, &type8)) return false; type = type8; if (type != 0) { /* wire length does not include type byte */ if (!mbuf_get_uint32be(&hdr, &len)) return false; len++; got = NEW_HEADER_LEN; } else { if (!mbuf_get_byte(&hdr, &type8)) return false; if (type8 != 0) { log_noise("get_header: unknown special pkt"); return false; } /* don't tolerate partial pkt */ if (mbuf_avail_for_read(&hdr) < OLD_HEADER_LEN - 2) { log_noise("get_header: less than 8 bytes for special pkt"); return false; } if (!mbuf_get_uint16be(&hdr, &len16)) return false; len = len16; if (!mbuf_get_uint32be(&hdr, &code)) return false; if (code == PKT_CANCEL) { type = PKT_CANCEL; } else if (code == PKT_SSLREQ) { type = PKT_SSLREQ; } else if ((code >> 16) == 3 && (code & 0xFFFF) < 2) { type = PKT_STARTUP; } else if (code == PKT_STARTUP_V2) { type = PKT_STARTUP_V2; } else { log_noise("get_header: unknown special pkt: len=%u code=%u", len, code); return false; } got = OLD_HEADER_LEN; } /* don't believe nonsense */ if (len < got || len > cf_max_packet_size) return false; /* store pkt info */ pkt->type = type; pkt->len = len; /* fill pkt with only data for this packet */ if (len > mbuf_avail_for_read(data)) { avail = mbuf_avail_for_read(data); } else { avail = len; } if (!mbuf_slice(data, avail, &pkt->data)) return false; /* tag header as read */ return mbuf_get_bytes(&pkt->data, got, &ptr); } /* * Send error message packet to client. */ bool send_pooler_error(PgSocket *client, bool send_ready, const char *msg) { uint8_t tmpbuf[512]; PktBuf buf; if (cf_log_pooler_errors) slog_warning(client, "Pooler Error: %s", msg); pktbuf_static(&buf, tmpbuf, sizeof(tmpbuf)); pktbuf_write_generic(&buf, 'E', "cscscsc", 'S', "ERROR", 'C', "08P01", 'M', msg, 0); if (send_ready) pktbuf_write_ReadyForQuery(&buf); return pktbuf_send_immediate(&buf, client); } /* * Parse server error message and log it. */ void parse_server_error(PktHdr *pkt, const char **level_p, const char **msg_p) { const char *level = NULL, *msg = NULL, *val; uint8_t type; while (mbuf_avail_for_read(&pkt->data)) { if (!mbuf_get_byte(&pkt->data, &type)) break; if (type == 0) break; if (!mbuf_get_string(&pkt->data, &val)) break; if (type == 'S') { level = val; } else if (type == 'M') { msg = val; } } *level_p = level; *msg_p = msg; } void log_server_error(const char *note, PktHdr *pkt) { const char *level = NULL, *msg = NULL; parse_server_error(pkt, &level, &msg); if (!msg || !level) { log_error("%s: partial error message, cannot log", note); } else { log_error("%s: %s: %s", note, level, msg); } } /* * Preparation of welcome message for client connection. */ /* add another server parameter packet to cache */ bool add_welcome_parameter(PgPool *pool, const char *key, const char *val) { PktBuf *msg = pool->welcome_msg; if (pool->welcome_msg_ready) return true; if (!msg) { msg = pktbuf_dynamic(128); if (!msg) return false; pool->welcome_msg = msg; } /* first packet must be AuthOk */ 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); return !msg->failed; } /* all parameters processed */ void finish_welcome_msg(PgSocket *server) { PgPool *pool = server->pool; if (pool->welcome_msg_ready) return; pool->welcome_msg_ready = 1; } bool welcome_client(PgSocket *client) { int res; PgPool *pool = client->pool; const PktBuf *pmsg = pool->welcome_msg; PktBuf *msg; slog_noise(client, "P: welcome_client"); /* 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); /* give each client its own cancel key */ get_random_bytes(client->cancel_key, 8); pktbuf_write_BackendKeyData(msg, client->cancel_key); /* 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_immediate(msg, client); if (!res) { disconnect_client(client, true, "failed to send welcome message"); return false; } return true; } /* * Password authentication for server */ static PgUser *get_srv_psw(PgSocket *server) { PgDatabase *db = server->pool->db; PgUser *user = server->pool->user; /* if forced user without password, use userlist psw */ if (!user->passwd[0] && db->forced_user) { PgUser *u2 = find_user(user->name); if (u2) return u2; } return user; } /* actual packet send */ static bool send_password(PgSocket *server, const char *enc_psw) { bool res; SEND_PasswordMessage(res, server, enc_psw); return res; } static bool login_clear_psw(PgSocket *server) { PgUser *user = get_srv_psw(server); slog_debug(server, "P: send clear password"); return send_password(server, user->passwd); } static bool login_md5_psw(PgSocket *server, const uint8_t *salt) { char txt[MD5_PASSWD_LEN + 1], *src; PgUser *user = get_srv_psw(server); slog_debug(server, "P: send md5 password"); if (!isMD5(user->passwd)) { pg_md5_encrypt(user->passwd, user->name, strlen(user->name), txt); src = txt + 3; } else { src = user->passwd + 3; } pg_md5_encrypt(src, (char *)salt, 4, txt); return send_password(server, txt); } /* answer server authentication request */ bool answer_authreq(PgSocket *server, PktHdr *pkt) { uint32_t cmd; const uint8_t *salt; bool res = false; /* authreq body must contain 32bit cmd */ if (mbuf_avail_for_read(&pkt->data) < 4) return false; if (!mbuf_get_uint32be(&pkt->data, &cmd)) return false; switch (cmd) { case AUTH_OK: slog_debug(server, "S: auth ok"); res = true; break; case AUTH_PLAIN: slog_debug(server, "S: req cleartext password"); res = login_clear_psw(server); break; case AUTH_MD5: slog_debug(server, "S: req md5-crypted psw"); if (!mbuf_get_bytes(&pkt->data, 4, &salt)) return false; res = login_md5_psw(server, salt); break; default: slog_error(server, "unknown/unsupported auth method: %d", cmd); res = false; break; } return res; } bool send_startup_packet(PgSocket *server) { PgDatabase *db = server->pool->db; const char *username = server->pool->user->name; PktBuf *pkt; pkt = pktbuf_temp(); pktbuf_write_StartupMessage(pkt, username, db->startup_params->buf, db->startup_params->write_pos); return pktbuf_send_immediate(pkt, server); } bool send_sslreq_packet(PgSocket *server) { int res; SEND_wrap(16, pktbuf_write_SSLRequest, res, server); return res; } int scan_text_result(struct MBuf *pkt, const char *tupdesc, ...) { const char *val = NULL; uint32_t len; uint16_t ncol; unsigned i, asked; va_list ap; int *int_p; uint64_t *long_p; const char **str_p; asked = strlen(tupdesc); if (!mbuf_get_uint16be(pkt, &ncol)) return -1; va_start(ap, tupdesc); for (i = 0; i < asked; i++) { if (i < ncol) { if (!mbuf_get_uint32be(pkt, &len)) { va_end(ap); return -1; } if ((int32_t)len < 0) { val = NULL; } else { if (!mbuf_get_chars(pkt, len, &val)) { va_end(ap); return -1; } } /* hack to zero-terminate the result */ if (val) { char *xval = (char *)val - 1; memmove(xval, val, len); xval[len] = 0; val = xval; } } else { /* tuple was shorter than requested */ val = NULL; } switch (tupdesc[i]) { case 'i': int_p = va_arg(ap, int *); *int_p = val ? atoi(val) : 0; break; case 'q': long_p = va_arg(ap, uint64_t *); *long_p = val ? atoll(val) : 0; break; case 's': str_p = va_arg(ap, const char **); *str_p = val; break; default: fatal("bad tupdesc: %s", tupdesc); } } va_end(ap); return ncol; } pgbouncer-1.7/src/stats.c0000664000175000017500000001263712561345127012400 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "bouncer.h" static struct event ev_stats; static usec_t old_stamp, new_stamp; static void reset_stats(PgStats *stat) { stat->server_bytes = 0; stat->client_bytes = 0; stat->request_count = 0; stat->query_time = 0; } static void stat_add(PgStats *total, PgStats *stat) { total->server_bytes += stat->server_bytes; total->client_bytes += stat->client_bytes; total->request_count += stat->request_count; total->query_time += stat->query_time; } static void calc_average(PgStats *avg, PgStats *cur, PgStats *old) { uint64_t qcount; usec_t dur = get_cached_time() - old_stamp; reset_stats(avg); if (dur <= 0) return; avg->request_count = USEC * (cur->request_count - old->request_count) / dur; avg->client_bytes = USEC * (cur->client_bytes - old->client_bytes) / dur; avg->server_bytes = USEC * (cur->server_bytes - old->server_bytes) / dur; qcount = cur->request_count - old->request_count; if (qcount > 0) avg->query_time = (cur->query_time - old->query_time) / qcount; } static void write_stats(PktBuf *buf, PgStats *stat, PgStats *old, char *dbname) { PgStats avg; calc_average(&avg, stat, old); pktbuf_write_DataRow(buf, "sqqqqqqqq", dbname, stat->request_count, stat->client_bytes, stat->server_bytes, stat->query_time, avg.request_count, avg.client_bytes, avg.server_bytes, avg.query_time); } bool admin_database_stats(PgSocket *client, struct StatList *pool_list) { PgPool *pool; struct List *item; PgDatabase *cur_db = NULL; PgStats st_total, st_db, old_db, old_total; int rows = 0; PktBuf *buf; reset_stats(&st_total); reset_stats(&st_db); reset_stats(&old_db); reset_stats(&old_total); buf = pktbuf_dynamic(512); if (!buf) { admin_error(client, "no mem"); return true; } pktbuf_write_RowDescription(buf, "sqqqqqqqq", "database", "total_requests", "total_received", "total_sent", "total_query_time", "avg_req", "avg_recv", "avg_sent", "avg_query"); statlist_for_each(item, pool_list) { pool = container_of(item, PgPool, head); if (!cur_db) cur_db = pool->db; if (pool->db != cur_db) { write_stats(buf, &st_db, &old_db, cur_db->name); rows ++; cur_db = pool->db; stat_add(&st_total, &st_db); stat_add(&old_total, &old_db); reset_stats(&st_db); reset_stats(&old_db); } stat_add(&st_db, &pool->stats); stat_add(&old_db, &pool->older_stats); } if (cur_db) { write_stats(buf, &st_db, &old_db, cur_db->name); stat_add(&st_total, &st_db); stat_add(&old_total, &old_db); rows ++; } admin_flush(client, buf, "SHOW"); return true; } bool show_stat_totals(PgSocket *client, struct StatList *pool_list) { PgPool *pool; struct List *item; PgStats st_total, old_total, avg; PktBuf *buf; reset_stats(&st_total); reset_stats(&old_total); buf = pktbuf_dynamic(512); if (!buf) { admin_error(client, "no mem"); return true; } statlist_for_each(item, pool_list) { pool = container_of(item, PgPool, head); stat_add(&st_total, &pool->stats); stat_add(&old_total, &pool->older_stats); } calc_average(&avg, &st_total, &old_total); pktbuf_write_RowDescription(buf, "sq", "name", "value"); #define WTOTAL(name) pktbuf_write_DataRow(buf, "sq", "total_" #name, st_total.name) #define WAVG(name) pktbuf_write_DataRow(buf, "sq", "avg_" #name, avg.name) WTOTAL(request_count); WTOTAL(client_bytes); WTOTAL(server_bytes); WTOTAL(query_time); WAVG(request_count); WAVG(client_bytes); WAVG(server_bytes); WAVG(query_time); admin_flush(client, buf, "SHOW"); return true; } static void refresh_stats(int s, short flags, void *arg) { struct List *item; PgPool *pool; struct timeval period = { cf_stats_period, 0 }; PgStats old_total, cur_total, avg; reset_stats(&old_total); reset_stats(&cur_total); old_stamp = new_stamp; new_stamp = get_cached_time(); statlist_for_each(item, &pool_list) { pool = container_of(item, PgPool, head); pool->older_stats = pool->newer_stats; pool->newer_stats = pool->stats; stat_add(&cur_total, &pool->stats); stat_add(&old_total, &pool->older_stats); } calc_average(&avg, &cur_total, &old_total); /* send totals to logfile */ log_info("Stats: %" PRIu64 " req/s," " in %" PRIu64 " b/s," " out %" PRIu64 " b/s," "query %" PRIu64 " us", avg.request_count, avg.client_bytes, avg.server_bytes, avg.query_time); safe_evtimer_add(&ev_stats, &period); } void stats_setup(void) { struct timeval period = { cf_stats_period, 0 }; new_stamp = get_cached_time(); old_stamp = new_stamp - USEC; /* launch stats */ evtimer_set(&ev_stats, refresh_stats, NULL); safe_evtimer_add(&ev_stats, &period); } pgbouncer-1.7/src/client.c0000664000175000017500000004612412627600700012510 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Client connection handling */ #include "bouncer.h" #include static const char *hdr2hex(const struct MBuf *data, char *buf, unsigned buflen) { const uint8_t *bin = data->data + data->read_pos; unsigned int dlen; dlen = mbuf_avail_for_read(data); return bin2hex(bin, dlen, buf, buflen); } static bool check_client_passwd(PgSocket *client, const char *passwd) { char md5[MD5_PASSWD_LEN + 1]; PgUser *user = client->auth_user; int auth_type = client->client_auth_type; /* disallow empty passwords */ if (!*passwd || !*user->passwd) return false; switch (auth_type) { case AUTH_PLAIN: return strcmp(user->passwd, passwd) == 0; case AUTH_MD5: if (strlen(passwd) != MD5_PASSWD_LEN) return false; if (!isMD5(user->passwd)) pg_md5_encrypt(user->passwd, user->name, strlen(user->name), user->passwd); pg_md5_encrypt(user->passwd + 3, (char *)client->tmp_login_salt, 4, md5); return strcmp(md5, passwd) == 0; } return false; } static bool send_client_authreq(PgSocket *client) { uint8_t saltlen = 0; int res; int auth_type = client->client_auth_type; if (auth_type == AUTH_MD5) { saltlen = 4; get_random_bytes((void*)client->tmp_login_salt, saltlen); } else if (auth_type == AUTH_PLAIN) { /* nothing to do */ } else { return false; } SEND_generic(res, client, 'R', "ib", auth_type, client->tmp_login_salt, saltlen); if (!res) disconnect_client(client, false, "failed to send auth req"); return res; } static void start_auth_request(PgSocket *client, const char *username) { int res; PktBuf *buf; /* have to fetch user info from db */ client->pool = get_pool(client->db, client->db->auth_user); if (!find_server(client)) { client->wait_for_user_conn = true; return; } slog_noise(client, "Doing auth_conn query"); client->wait_for_user_conn = false; client->wait_for_user = true; if (!sbuf_pause(&client->sbuf)) { release_server(client->link); disconnect_client(client, true, "pause failed"); return; } client->link->ready = 0; res = 0; buf = pktbuf_dynamic(512); if (buf) { pktbuf_write_ExtQuery(buf, cf_auth_query, 1, username); res = pktbuf_send_immediate(buf, client->link); pktbuf_free(buf); /* * Should do instead: * res = pktbuf_send_queued(buf, client->link); * but that needs better integration with SBuf. */ } if (!res) disconnect_server(client->link, false, "unable to send login query"); } static bool login_via_cert(PgSocket *client) { struct tls *tls = client->sbuf.tls; if (!tls) { disconnect_client(client, true, "TLS connection required"); return false; } if (!tls_peer_cert_provided(client->sbuf.tls)) { disconnect_client(client, true, "TLS client certificate required"); return false; } log_debug("TLS cert login: %s", tls_peer_cert_subject(client->sbuf.tls)); if (!tls_peer_cert_contains_name(client->sbuf.tls, client->auth_user->name)) { disconnect_client(client, true, "TLS certificate name mismatch"); return false; } /* login successful */ return finish_client_login(client); } static bool login_as_unix_peer(PgSocket *client) { if (!pga_is_unix(&client->remote_addr)) goto fail; if (!check_unix_peer_name(sbuf_socket(&client->sbuf), client->auth_user->name)) goto fail; return finish_client_login(client); fail: disconnect_client(client, true, "unix socket login rejected"); return false; } static bool finish_set_pool(PgSocket *client, bool takeover) { PgUser *user = client->auth_user; bool ok = false; int auth; /* pool user may be forced */ if (client->db->forced_user) { user = client->db->forced_user; } client->pool = get_pool(client->db, user); if (!client->pool) { disconnect_client(client, true, "no memory for pool"); return false; } if (cf_log_connections) { if (client->sbuf.tls) { char infobuf[96] = ""; tls_get_connection_info(client->sbuf.tls, infobuf, sizeof infobuf); slog_info(client, "login attempt: db=%s user=%s tls=%s", client->db->name, client->auth_user->name, infobuf); } else { slog_info(client, "login attempt: db=%s user=%s tls=no", client->db->name, client->auth_user->name); } } if (!check_fast_fail(client)) return false; if (takeover) return true; if (client->pool->db->admin) { if (!admin_post_login(client)) return false; } if (client->own_user) return finish_client_login(client); auth = cf_auth_type; if (auth == AUTH_HBA) { auth = hba_eval(parsed_hba, &client->remote_addr, !!client->sbuf.tls, client->db->name, client->auth_user->name); } /* remember method */ client->client_auth_type = auth; switch (auth) { case AUTH_ANY: case AUTH_TRUST: ok = finish_client_login(client); break; case AUTH_PLAIN: case AUTH_MD5: ok = send_client_authreq(client); break; case AUTH_CERT: ok = login_via_cert(client); break; case AUTH_PEER: ok = login_as_unix_peer(client); break; default: disconnect_client(client, true, "login rejected"); ok = false; } return ok; } bool set_pool(PgSocket *client, const char *dbname, const char *username, const char *password, bool takeover) { /* find database */ client->db = find_database(dbname); if (!client->db) { client->db = register_auto_database(dbname); if (!client->db) { disconnect_client(client, true, "No such database: %s", dbname); if (cf_log_connections) slog_info(client, "login failed: db=%s user=%s", dbname, username); return false; } else { slog_info(client, "registered new auto-database: db = %s", dbname ); } } /* are new connections allowed? */ if (client->db->db_disabled) { disconnect_client(client, true, "database does not allow connections: %s", dbname); return false; } if (client->db->admin) { if (admin_pre_login(client, username)) return finish_set_pool(client, takeover); } /* find user */ if (cf_auth_type == AUTH_ANY) { /* ignore requested user */ if (client->db->forced_user == NULL) { slog_error(client, "auth_type=any requires forced user"); disconnect_client(client, true, "bouncer config error"); return false; } client->auth_user = client->db->forced_user; } else { /* the user clients wants to log in as */ client->auth_user = find_user(username); if (!client->auth_user && client->db->auth_user) { if (takeover) { client->auth_user = add_db_user(client->db, username, password); return finish_set_pool(client, takeover); } start_auth_request(client, username); return false; } if (!client->auth_user) { disconnect_client(client, true, "No such user: %s", username); if (cf_log_connections) slog_info(client, "login failed: db=%s user=%s", dbname, username); return false; } } return finish_set_pool(client, takeover); } bool handle_auth_response(PgSocket *client, PktHdr *pkt) { uint16_t columns; uint32_t length; const char *username, *password; PgUser user; PgSocket *server = client->link; switch(pkt->type) { case 'T': /* RowDescription */ if (!mbuf_get_uint16be(&pkt->data, &columns)) { disconnect_server(server, false, "bad packet"); return false; } if (columns != 2u) { disconnect_server(server, false, "expected 1 column from login query, not %hu", columns); return false; } break; case 'D': /* DataRow */ memset(&user, 0, sizeof(user)); if (!mbuf_get_uint16be(&pkt->data, &columns)) { disconnect_server(server, false, "bad packet"); return false; } if (columns != 2u) { disconnect_server(server, false, "expected 1 column from login query, not %hu", columns); return false; } if (!mbuf_get_uint32be(&pkt->data, &length)) { disconnect_server(server, false, "bad packet"); return false; } if (!mbuf_get_chars(&pkt->data, length, &username)) { disconnect_server(server, false, "bad packet"); return false; } if (sizeof(user.name) - 1 < length) length = sizeof(user.name) - 1; memcpy(user.name, username, length); if (!mbuf_get_uint32be(&pkt->data, &length)) { disconnect_server(server, false, "bad packet"); return false; } if (length == (uint32_t)-1) { /* * NULL - set an md5 password with an impossible value, * so that nothing will ever match */ password = "md5"; length = 3; } else { if (!mbuf_get_chars(&pkt->data, length, &password)) { disconnect_server(server, false, "bad packet"); return false; } } if (sizeof(user.passwd) - 1 < length) length = sizeof(user.passwd) - 1; memcpy(user.passwd, password, length); client->auth_user = add_db_user(client->db, user.name, user.passwd); if (!client->auth_user) { disconnect_server(server, false, "unable to allocate new user for auth"); return false; } break; case 'N': /* NoticeResponse */ break; case 'C': /* CommandComplete */ break; case '1': /* ParseComplete */ break; case '2': /* BindComplete */ break; case 'Z': /* ReadyForQuery */ sbuf_prepare_skip(&client->link->sbuf, pkt->len); if (!client->auth_user) { if (cf_log_connections) slog_info(client, "login failed: db=%s", client->db->name); disconnect_client(client, true, "No such user"); } else { slog_noise(client, "auth query complete"); client->link->resetting = true; sbuf_continue(&client->sbuf); } /* * either sbuf_continue or disconnect_client could disconnect the server * way down in their bowels of other callbacks. so check that, and * return appropriately (similar to reuse_on_release) */ if (server->state == SV_FREE || server->state == SV_JUSTFREE) return false; return true; default: disconnect_server(server, false, "unexpected response from login query"); return false; } sbuf_prepare_skip(&server->sbuf, pkt->len); return true; } static void set_appname(PgSocket *client, const char *app_name) { char buf[400], abuf[300]; const char *details; if (cf_application_name_add_host) { /* give app a name */ if (!app_name) app_name = "app"; /* add details */ details = pga_details(&client->remote_addr, abuf, sizeof(abuf)); snprintf(buf, sizeof(buf), "%s - %s", app_name, details); app_name = buf; } if (app_name) { slog_debug(client, "using application_name: %s", app_name); varcache_set(&client->vars, "application_name", app_name); } } static bool decide_startup_pool(PgSocket *client, PktHdr *pkt) { const char *username = NULL, *dbname = NULL; const char *key, *val; bool ok; bool appname_found = false; while (1) { ok = mbuf_get_string(&pkt->data, &key); if (!ok || *key == 0) break; ok = mbuf_get_string(&pkt->data, &val); if (!ok) break; if (strcmp(key, "database") == 0) { slog_debug(client, "got var: %s=%s", key, val); dbname = val; } else if (strcmp(key, "user") == 0) { slog_debug(client, "got var: %s=%s", key, val); username = val; } else if (strcmp(key, "application_name") == 0) { set_appname(client, val); appname_found = true; } else if (varcache_set(&client->vars, key, val)) { slog_debug(client, "got var: %s=%s", key, val); } else if (strlist_contains(cf_ignore_startup_params, key)) { slog_debug(client, "ignoring startup parameter: %s=%s", key, val); } else { slog_warning(client, "unsupported startup parameter: %s=%s", key, val); disconnect_client(client, true, "Unsupported startup parameter: %s", key); return false; } } if (!username || !username[0]) { disconnect_client(client, true, "No username supplied"); return false; } /* if missing dbname, default to username */ if (!dbname || !dbname[0]) dbname = username; /* create application_name if requested */ if (!appname_found) set_appname(client, NULL); /* check if limit allows, don't limit admin db nb: new incoming conn will be attached to PgSocket, thus get_active_client_count() counts it */ if (get_active_client_count() > cf_max_client_conn) { if (strcmp(dbname, "pgbouncer") != 0) { disconnect_client(client, true, "no more connections allowed (max_client_conn)"); return false; } } /* find pool */ return set_pool(client, dbname, username, "", false); } /* decide on packets of client in login phase */ static bool handle_client_startup(PgSocket *client, PktHdr *pkt) { const char *passwd; const uint8_t *key; bool ok; SBuf *sbuf = &client->sbuf; /* don't tolerate partial packets */ if (incomplete_pkt(pkt)) { disconnect_client(client, true, "client sent partial pkt in startup phase"); return false; } if (client->wait_for_welcome) { if (finish_client_login(client)) { /* the packet was already parsed */ sbuf_prepare_skip(sbuf, pkt->len); return true; } else { return false; } } switch (pkt->type) { case PKT_SSLREQ: slog_noise(client, "C: req SSL"); if (client->sbuf.tls) { disconnect_client(client, false, "SSL req inside SSL"); return false; } if (cf_client_tls_sslmode != SSLMODE_DISABLED) { slog_noise(client, "P: SSL ack"); if (!sbuf_answer(&client->sbuf, "S", 1)) { disconnect_client(client, false, "failed to ack SSL"); return false; } if (!sbuf_tls_accept(&client->sbuf)) { disconnect_client(client, false, "failed to accept SSL"); return false; } break; } /* reject SSL attempt */ slog_noise(client, "P: nak"); if (!sbuf_answer(&client->sbuf, "N", 1)) { disconnect_client(client, false, "failed to nak SSL"); return false; } break; case PKT_STARTUP_V2: disconnect_client(client, true, "Old V2 protocol not supported"); return false; case PKT_STARTUP: /* require SSL except on unix socket */ if (cf_client_tls_sslmode >= SSLMODE_REQUIRE && !client->sbuf.tls && !pga_is_unix(&client->remote_addr)) { disconnect_client(client, true, "SSL required"); return false; } if (client->pool && !client->wait_for_user_conn && !client->wait_for_user) { disconnect_client(client, true, "client re-sent startup pkt"); return false; } if (client->wait_for_user) { client->wait_for_user = false; if (!finish_set_pool(client, false)) return false; } else if (!decide_startup_pool(client, pkt)) { return false; } break; case 'p': /* PasswordMessage */ /* too early */ if (!client->auth_user) { disconnect_client(client, true, "client password pkt before startup packet"); return false; } ok = mbuf_get_string(&pkt->data, &passwd); if (ok && check_client_passwd(client, passwd)) { if (!finish_client_login(client)) return false; } else { disconnect_client(client, true, "Auth failed"); return false; } break; case PKT_CANCEL: if (mbuf_avail_for_read(&pkt->data) == BACKENDKEY_LEN && mbuf_get_bytes(&pkt->data, BACKENDKEY_LEN, &key)) { memcpy(client->cancel_key, key, BACKENDKEY_LEN); accept_cancel_request(client); } else { disconnect_client(client, false, "bad cancel request"); } return false; default: disconnect_client(client, false, "bad packet"); return false; } sbuf_prepare_skip(sbuf, pkt->len); client->request_time = get_cached_time(); return true; } /* decide on packets of logged-in client */ static bool handle_client_work(PgSocket *client, PktHdr *pkt) { SBuf *sbuf = &client->sbuf; int rfq_delta = 0; switch (pkt->type) { /* one-packet queries */ case 'Q': /* Query */ if (cf_disable_pqexec) { slog_error(client, "Client used 'Q' packet type."); disconnect_client(client, true, "PQexec disallowed"); return false; } case 'F': /* FunctionCall */ /* request immediate response from server */ case 'S': /* Sync */ rfq_delta++; break; case 'H': /* Flush */ break; /* copy end markers */ case 'c': /* CopyDone(F/B) */ case 'f': /* CopyFail(F/B) */ break; /* * extended protocol allows server (and thus pooler) * to buffer packets until sync or flush is sent by client */ case 'P': /* Parse */ case 'E': /* Execute */ case 'C': /* Close */ case 'B': /* Bind */ case 'D': /* Describe */ case 'd': /* CopyData(F/B) */ break; /* client wants to go away */ default: slog_error(client, "unknown pkt from client: %d/0x%x", pkt->type, pkt->type); disconnect_client(client, true, "unknown pkt"); return false; case 'X': /* Terminate */ disconnect_client(client, false, "client close request"); return false; } /* update stats */ if (!client->query_start) { client->pool->stats.request_count++; client->query_start = get_cached_time(); } if (client->pool->db->admin) return admin_handle_client(client, pkt); /* acquire server */ if (!find_server(client)) return false; /* postpone rfq change until certain that client will not be paused */ if (rfq_delta) { client->expect_rfq_count += rfq_delta; } client->pool->stats.client_bytes += pkt->len; /* tag the server as dirty */ client->link->ready = false; client->link->idle_tx = false; /* forward the packet */ sbuf_prepare_send(sbuf, &client->link->sbuf, pkt->len); return true; } /* callback from SBuf */ bool client_proto(SBuf *sbuf, SBufEvent evtype, struct MBuf *data) { bool res = false; PgSocket *client = container_of(sbuf, PgSocket, sbuf); PktHdr pkt; Assert(!is_server_socket(client)); Assert(client->sbuf.sock); Assert(client->state != CL_FREE); /* may happen if close failed */ if (client->state == CL_JUSTFREE) return false; switch (evtype) { case SBUF_EV_CONNECT_OK: case SBUF_EV_CONNECT_FAILED: /* ^ those should not happen */ case SBUF_EV_RECV_FAILED: disconnect_client(client, false, "client unexpected eof"); break; case SBUF_EV_SEND_FAILED: disconnect_server(client->link, false, "Server connection closed"); break; case SBUF_EV_READ: /* Wait until full packet headers is available. */ if (incomplete_header(data)) { slog_noise(client, "C: got partial header, trying to wait a bit"); return false; } if (!get_header(data, &pkt)) { char hex[8*2 + 1]; disconnect_client(client, true, "bad packet header: '%s'", hdr2hex(data, hex, sizeof(hex))); return false; } slog_noise(client, "pkt='%c' len=%d", pkt_desc(&pkt), pkt.len); client->request_time = get_cached_time(); switch (client->state) { case CL_LOGIN: res = handle_client_startup(client, &pkt); break; case CL_ACTIVE: if (client->wait_for_welcome) res = handle_client_startup(client, &pkt); else res = handle_client_work(client, &pkt); break; case CL_WAITING: fatal("why waiting client in client_proto()"); default: fatal("bad client state: %d", client->state); } break; case SBUF_EV_FLUSH: /* client is not interested in it */ break; case SBUF_EV_PKT_CALLBACK: /* unused ATM */ break; case SBUF_EV_TLS_READY: sbuf_continue(&client->sbuf); res = true; break; } return res; } pgbouncer-1.7/src/pktbuf.c0000664000175000017500000002317712572127367012544 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Packet writing and sending. */ #include "bouncer.h" void pktbuf_free(PktBuf *buf) { if (!buf || buf->fixed_buf) return; log_debug("pktbuf_free(%p)", buf); if (buf->buf) free(buf->buf); if (buf->ev) free(buf->ev); free(buf); } PktBuf *pktbuf_dynamic(int start_len) { PktBuf *buf = zmalloc(sizeof(PktBuf)); log_debug("pktbuf_dynamic(%d): %p", start_len, buf); if (!buf) return NULL; buf->ev = zmalloc(sizeof(*buf->ev)); if (!buf->ev) { pktbuf_free(buf); return NULL; } buf->buf = malloc(start_len); if (!buf->buf) { pktbuf_free(buf); return NULL; } buf->buf_len = start_len; 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->buf = data; buf->buf_len = len; 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_immediate(PktBuf *buf, PgSocket *sk) { uint8_t *pos = buf->buf + buf->send_pos; int amount = buf->write_pos - buf->send_pos; int res; if (buf->failed) return false; res = sbuf_op_send(&sk->sbuf, pos, amount); if (res < 0) { log_debug("pktbuf_send_immediate: %s", strerror(errno)); } return res == amount; } static void pktbuf_send_func(int fd, short flags, void *arg) { PktBuf *buf = arg; SBuf *sbuf = &buf->queued_dst->sbuf; int amount, res; log_debug("pktbuf_send_func(%d, %d, %p)", fd, (int)flags, buf); if (buf->failed) return; amount = buf->write_pos - buf->send_pos; res = sbuf_op_send(sbuf, buf->buf + buf->send_pos, amount); if (res < 0) { if (errno == EAGAIN) { res = 0; } else { log_error("pktbuf_send_func: %s", strerror(errno)); pktbuf_free(buf); return; } } buf->send_pos += res; if (buf->send_pos < buf->write_pos) { event_set(buf->ev, fd, EV_WRITE, pktbuf_send_func, buf); res = event_add(buf->ev, NULL); if (res < 0) { log_error("pktbuf_send_func: %s", strerror(errno)); pktbuf_free(buf); } } else { pktbuf_free(buf); } } bool pktbuf_send_queued(PktBuf *buf, PgSocket *sk) { Assert(!buf->sending); Assert(!buf->fixed_buf); if (buf->failed) { pktbuf_free(buf); return send_pooler_error(sk, true, "result prepare failed"); } else { buf->sending = 1; buf->queued_dst = sk; pktbuf_send_func(sk->sbuf.sock, EV_WRITE, buf); return true; } } static void make_room(PktBuf *buf, int len) { int newlen = buf->buf_len; int need = buf->write_pos + len; void *ptr; if (newlen >= need) return; if (buf->failed) return; if (buf->fixed_buf) { buf->failed = 1; return; } while (newlen < need) newlen = newlen * 2; log_debug("make_room(%p, %d): realloc newlen=%d", buf, len, newlen); ptr = realloc(buf->buf, newlen); if (!ptr) { buf->failed = 1; } else { buf->buf = ptr; buf->buf_len = newlen; } } void pktbuf_put_char(PktBuf *buf, char val) { make_room(buf, 1); if (buf->failed) return; buf->buf[buf->write_pos++] = val; } void pktbuf_put_uint16(PktBuf *buf, uint16_t val) { make_room(buf, 4); if (buf->failed) return; buf->buf[buf->write_pos++] = (val >> 8) & 255; buf->buf[buf->write_pos++] = val & 255; } void pktbuf_put_uint32(PktBuf *buf, uint32_t val) { uint8_t *pos; make_room(buf, 4); if (buf->failed) return; pos = buf->buf + buf->write_pos; pos[0] = (val >> 24) & 255; pos[1] = (val >> 16) & 255; pos[2] = (val >> 8) & 255; pos[3] = val & 255; buf->write_pos += 4; } void pktbuf_put_uint64(PktBuf *buf, uint64_t val) { pktbuf_put_uint32(buf, val >> 32); pktbuf_put_uint32(buf, (uint32_t)val); } void pktbuf_put_bytes(PktBuf *buf, const void *data, int len) { make_room(buf, len); if (buf->failed) return; memcpy(buf->buf + buf->write_pos, data, len); buf->write_pos += len; } void pktbuf_put_string(PktBuf *buf, const char *str) { int len = strlen(str); pktbuf_put_bytes(buf, str, len + 1); } /* * write header, remember pos to write length later. */ void pktbuf_start_packet(PktBuf *buf, int type) { if (buf->failed) return; if (type < 256) { /* new-style packet */ pktbuf_put_char(buf, type); buf->pktlen_pos = buf->write_pos; pktbuf_put_uint32(buf, 0); } else { /* old-style packet */ buf->pktlen_pos = buf->write_pos; pktbuf_put_uint32(buf, 0); pktbuf_put_uint32(buf, type); } } void pktbuf_finish_packet(PktBuf *buf) { uint8_t *pos; unsigned len; if (buf->failed) return; len = buf->write_pos - buf->pktlen_pos; pos = buf->buf + buf->pktlen_pos; buf->pktlen_pos = 0; *pos++ = (len >> 24) & 255; *pos++ = (len >> 16) & 255; *pos++ = (len >> 8) & 255; *pos++ = len & 255; } /* types: * c - char/byte * h - uint16 * i - uint32 * q - uint64 * s - Cstring * b - bytes */ void pktbuf_write_generic(PktBuf *buf, int type, const char *pktdesc, ...) { va_list ap; int len; const char *adesc = pktdesc; uint8_t *bin; pktbuf_start_packet(buf, type); va_start(ap, pktdesc); while (*adesc) { switch (*adesc) { case 'c': pktbuf_put_char(buf, va_arg(ap, int)); break; case 'h': pktbuf_put_uint16(buf, va_arg(ap, int)); break; case 'i': pktbuf_put_uint32(buf, va_arg(ap, int)); break; case 'q': pktbuf_put_uint64(buf, va_arg(ap, uint64_t)); break; case 's': pktbuf_put_string(buf, va_arg(ap, char *)); break; case 'b': bin = va_arg(ap, uint8_t *); len = va_arg(ap, int); pktbuf_put_bytes(buf, bin, len); break; default: fatal("bad pktdesc: %s", pktdesc); } adesc++; } va_end(ap); /* set correct length */ pktbuf_finish_packet(buf); } /* send resultset column info * tupdesc keys: * 'i' - int4 * 'q' - int8 * 's' - string * 'T' - usec_t to date */ void pktbuf_write_RowDescription(PktBuf *buf, const char *tupdesc, ...) { va_list ap; char *name; int i, ncol = strlen(tupdesc); log_noise("write RowDescription"); pktbuf_start_packet(buf, 'T'); pktbuf_put_uint16(buf, ncol); va_start(ap, tupdesc); for (i = 0; i < ncol; i++) { name = va_arg(ap, char *); /* Fields: name, reloid, colnr, oid, typsize, typmod, fmt */ pktbuf_put_string(buf, name); pktbuf_put_uint32(buf, 0); pktbuf_put_uint16(buf, 0); if (tupdesc[i] == 's') { pktbuf_put_uint32(buf, TEXTOID); pktbuf_put_uint16(buf, -1); } else if (tupdesc[i] == 'i') { pktbuf_put_uint32(buf, INT4OID); pktbuf_put_uint16(buf, 4); } else if (tupdesc[i] == 'q') { pktbuf_put_uint32(buf, INT8OID); pktbuf_put_uint16(buf, 8); } else if (tupdesc[i] == 'T') { pktbuf_put_uint32(buf, TEXTOID); pktbuf_put_uint16(buf, -1); } else { fatal("bad tupdesc"); } pktbuf_put_uint32(buf, 0); pktbuf_put_uint16(buf, 0); } va_end(ap); /* set correct length */ pktbuf_finish_packet(buf); } /* * send DataRow. * * tupdesc keys: * 'i' - int4 * 'q' - int8 * 's' - string * 'T' - usec_t to date */ void pktbuf_write_DataRow(PktBuf *buf, const char *tupdesc, ...) { char tmp[32]; const char *val = NULL; int i, len, ncol = strlen(tupdesc); va_list ap; pktbuf_start_packet(buf, 'D'); pktbuf_put_uint16(buf, ncol); va_start(ap, tupdesc); for (i = 0; i < ncol; i++) { if (tupdesc[i] == 'i') { snprintf(tmp, sizeof(tmp), "%d", va_arg(ap, int)); val = tmp; } else if (tupdesc[i] == 'q') { snprintf(tmp, sizeof(tmp), "%" PRIu64, va_arg(ap, uint64_t)); val = tmp; } else if (tupdesc[i] == 's') { val = va_arg(ap, char *); } else if (tupdesc[i] == 'T') { usec_t time = va_arg(ap, usec_t); val = format_time_s(time, tmp, sizeof(tmp)); } else { fatal("bad tupdesc: %s", tupdesc); } if (val) { len = strlen(val); pktbuf_put_uint32(buf, len); pktbuf_put_bytes(buf, val, len); } else { /* NULL */ pktbuf_put_uint32(buf, -1); } } va_end(ap); pktbuf_finish_packet(buf); } /* * Send Parse+Bind+Execute with string parameters. */ void pktbuf_write_ExtQuery(PktBuf *buf, const char *query, int nargs, ...) { va_list ap; const char *val; int len, i; /* Parse */ pktbuf_write_generic(buf, 'P', "csh", 0, query, 0); /* Bind */ pktbuf_start_packet(buf, 'B'); pktbuf_put_char(buf, 0); /* portal name */ pktbuf_put_char(buf, 0); /* query name */ pktbuf_put_uint16(buf, 0); /* number of parameter format codes */ pktbuf_put_uint16(buf, nargs); /* number of parameter values */ va_start(ap, nargs); for (i = 0; i < nargs; i++) { val = va_arg(ap, char *); len = strlen(val); pktbuf_put_uint32(buf, len); pktbuf_put_bytes(buf, val, len); } va_end(ap); pktbuf_put_uint16(buf, 0); /* number of result-column format codes */ pktbuf_finish_packet(buf); /* Describe */ pktbuf_write_generic(buf, 'D', "cc", 'P', 0); /* Execute */ pktbuf_write_generic(buf, 'E', "ci", 0, 0); /* Sync */ pktbuf_write_generic(buf, 'S', ""); } pgbouncer-1.7/src/objects.c0000664000175000017500000011751412571104044012663 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Herding objects between lists happens here. */ #include "bouncer.h" /* those items will be allocated as needed, never freed */ STATLIST(user_list); STATLIST(database_list); STATLIST(pool_list); struct AATree user_tree; /* * client and server objects will be pre-allocated * they are always in either active or free lists * in addition to others. */ STATLIST(login_client_list); struct Slab *server_cache; struct Slab *client_cache; struct Slab *db_cache; struct Slab *pool_cache; struct Slab *user_cache; struct Slab *iobuf_cache; /* * libevent may still report events when event_del() * is called from somewhere else. So hide just freed * PgSockets for one loop. */ static STATLIST(justfree_client_list); static STATLIST(justfree_server_list); /* init autodb idle list */ STATLIST(autodatabase_idle_list); /* fast way to get number of active clients */ int get_active_client_count(void) { return slab_active_count(client_cache); } /* fast way to get number of active servers */ int get_active_server_count(void) { return slab_active_count(server_cache); } static void construct_client(void *obj) { PgSocket *client = obj; memset(client, 0, sizeof(PgSocket)); list_init(&client->head); sbuf_init(&client->sbuf, client_proto); client->state = CL_FREE; } static void construct_server(void *obj) { PgSocket *server = obj; memset(server, 0, sizeof(PgSocket)); list_init(&server->head); sbuf_init(&server->sbuf, server_proto); server->state = SV_FREE; } /* compare string with PgUser->name, for usage with btree */ static int user_node_cmp(uintptr_t userptr, struct AANode *node) { const char *name = (const char *)userptr; PgUser *user = container_of(node, PgUser, tree_node); return strcmp(name, user->name); } /* destroy PgUser, for usage with btree */ static void user_node_release(struct AANode *node, void *arg) { PgUser *user = container_of(node, PgUser, tree_node); slab_free(user_cache, user); } /* initialization before config loading */ void init_objects(void) { aatree_init(&user_tree, user_node_cmp, NULL); user_cache = slab_create("user_cache", sizeof(PgUser), 0, NULL, USUAL_ALLOC); db_cache = slab_create("db_cache", sizeof(PgDatabase), 0, NULL, USUAL_ALLOC); pool_cache = slab_create("pool_cache", sizeof(PgPool), 0, NULL, USUAL_ALLOC); if (!user_cache || !db_cache || !pool_cache) fatal("cannot create initial caches"); } static void do_iobuf_reset(void *arg) { IOBuf *io = arg; iobuf_reset(io); } /* initialization after config loading */ void init_caches(void) { server_cache = slab_create("server_cache", sizeof(PgSocket), 0, construct_server, USUAL_ALLOC); client_cache = slab_create("client_cache", sizeof(PgSocket), 0, construct_client, USUAL_ALLOC); iobuf_cache = slab_create("iobuf_cache", IOBUF_SIZE, 0, do_iobuf_reset, USUAL_ALLOC); } /* state change means moving between lists */ void change_client_state(PgSocket *client, SocketState newstate) { PgPool *pool = client->pool; /* remove from old location */ switch (client->state) { case CL_FREE: break; case CL_JUSTFREE: statlist_remove(&justfree_client_list, &client->head); break; case CL_LOGIN: if (newstate == CL_WAITING) newstate = CL_WAITING_LOGIN; statlist_remove(&login_client_list, &client->head); break; case CL_WAITING_LOGIN: if (newstate == CL_ACTIVE) newstate = CL_LOGIN; case CL_WAITING: statlist_remove(&pool->waiting_client_list, &client->head); break; case CL_ACTIVE: statlist_remove(&pool->active_client_list, &client->head); break; case CL_CANCEL: statlist_remove(&pool->cancel_req_list, &client->head); break; default: fatal("bad cur client state: %d", client->state); } client->state = newstate; /* put to new location */ switch (client->state) { case CL_FREE: varcache_clean(&client->vars); slab_free(client_cache, client); break; case CL_JUSTFREE: statlist_append(&justfree_client_list, &client->head); break; case CL_LOGIN: statlist_append(&login_client_list, &client->head); break; case CL_WAITING: case CL_WAITING_LOGIN: statlist_append(&pool->waiting_client_list, &client->head); break; case CL_ACTIVE: statlist_append(&pool->active_client_list, &client->head); break; case CL_CANCEL: statlist_append(&pool->cancel_req_list, &client->head); break; default: fatal("bad new client state: %d", client->state); } } /* state change means moving between lists */ void change_server_state(PgSocket *server, SocketState newstate) { PgPool *pool = server->pool; /* remove from old location */ switch (server->state) { case SV_FREE: break; case SV_JUSTFREE: statlist_remove(&justfree_server_list, &server->head); break; case SV_LOGIN: statlist_remove(&pool->new_server_list, &server->head); break; case SV_USED: statlist_remove(&pool->used_server_list, &server->head); break; case SV_TESTED: statlist_remove(&pool->tested_server_list, &server->head); break; case SV_IDLE: statlist_remove(&pool->idle_server_list, &server->head); break; case SV_ACTIVE: statlist_remove(&pool->active_server_list, &server->head); break; default: fatal("change_server_state: bad old server state: %d", server->state); } server->state = newstate; /* put to new location */ switch (server->state) { case SV_FREE: varcache_clean(&server->vars); slab_free(server_cache, server); break; case SV_JUSTFREE: statlist_append(&justfree_server_list, &server->head); break; case SV_LOGIN: statlist_append(&pool->new_server_list, &server->head); break; case SV_USED: /* use LIFO */ statlist_prepend(&pool->used_server_list, &server->head); break; case SV_TESTED: statlist_append(&pool->tested_server_list, &server->head); break; case SV_IDLE: if (server->close_needed || cf_server_round_robin) { /* try to avoid immediate usage then */ statlist_append(&pool->idle_server_list, &server->head); } else { /* otherwise use LIFO */ statlist_prepend(&pool->idle_server_list, &server->head); } break; case SV_ACTIVE: statlist_append(&pool->active_server_list, &server->head); break; default: fatal("bad server state"); } } /* compare pool names, for use with put_in_order */ static int cmp_pool(struct List *i1, struct List *i2) { PgPool *p1 = container_of(i1, PgPool, head); PgPool *p2 = container_of(i2, PgPool, head); if (p1->db != p2->db) return strcmp(p1->db->name, p2->db->name); if (p1->user != p2->user) return strcmp(p1->user->name, p2->user->name); return 0; } /* compare user names, for use with put_in_order */ static int cmp_user(struct List *i1, struct List *i2) { PgUser *u1 = container_of(i1, PgUser, head); PgUser *u2 = container_of(i2, PgUser, head); return strcmp(u1->name, u2->name); } /* compare db names, for use with put_in_order */ static int cmp_database(struct List *i1, struct List *i2) { PgDatabase *db1 = container_of(i1, PgDatabase, head); PgDatabase *db2 = container_of(i2, PgDatabase, head); return strcmp(db1->name, db2->name); } /* put elem into list in correct pos */ static void put_in_order(struct List *newitem, struct StatList *list, int (*cmpfn)(struct List *, struct List *)) { int res; struct List *item; statlist_for_each(item, list) { res = cmpfn(item, newitem); if (res == 0) { fatal("put_in_order: found existing elem"); } else if (res > 0) { statlist_put_before(list, newitem, item); return; } } statlist_append(list, newitem); } /* create new object if new, then return it */ PgDatabase *add_database(const char *name) { PgDatabase *db = find_database(name); /* create new object if needed */ if (db == NULL) { db = slab_alloc(db_cache); if (!db) return NULL; list_init(&db->head); if (strlcpy(db->name, name, sizeof(db->name)) >= sizeof(db->name)) { log_warning("Too long db name: %s", name); slab_free(db_cache, db); return NULL; } aatree_init(&db->user_tree, user_node_cmp, user_node_release); put_in_order(&db->head, &database_list, cmp_database); } return db; } /* register new auto database */ PgDatabase *register_auto_database(const char *name) { PgDatabase *db; int len; char *cs; if (!cf_autodb_connstr) return NULL; len = strlen(cf_autodb_connstr); cs = malloc(len + 1); if (!cs) return NULL; memcpy(cs, cf_autodb_connstr, len + 1); parse_database(NULL, (char*)name, cs); free(cs); db = find_database(name); if (db) { db->db_auto = 1; /* do not forget to check pool_size like in config_postprocess */ if (db->pool_size < 0) db->pool_size = cf_default_pool_size; if (db->res_pool_size < 0) db->res_pool_size = cf_res_pool_size; } return db; } /* add or update client users */ PgUser *add_user(const char *name, const char *passwd) { PgUser *user = find_user(name); if (user == NULL) { user = slab_alloc(user_cache); if (!user) return NULL; list_init(&user->head); list_init(&user->pool_list); safe_strcpy(user->name, name, sizeof(user->name)); put_in_order(&user->head, &user_list, cmp_user); aatree_insert(&user_tree, (uintptr_t)user->name, &user->tree_node); user->pool_mode = POOL_INHERIT; } safe_strcpy(user->passwd, passwd, sizeof(user->passwd)); return user; } /* add or update db users */ PgUser *add_db_user(PgDatabase *db, const char *name, const char *passwd) { PgUser *user = NULL; struct AANode *node; node = aatree_search(&db->user_tree, (uintptr_t)name); user = node ? container_of(node, PgUser, tree_node) : NULL; if (user == NULL) { user = slab_alloc(user_cache); if (!user) return NULL; list_init(&user->head); list_init(&user->pool_list); safe_strcpy(user->name, name, sizeof(user->name)); aatree_insert(&db->user_tree, (uintptr_t)user->name, &user->tree_node); user->pool_mode = POOL_INHERIT; } safe_strcpy(user->passwd, passwd, sizeof(user->passwd)); return user; } /* create separate user object for storing server user info */ PgUser *force_user(PgDatabase *db, const char *name, const char *passwd) { PgUser *user = db->forced_user; if (!user) { user = slab_alloc(user_cache); if (!user) return NULL; list_init(&user->head); list_init(&user->pool_list); user->pool_mode = POOL_INHERIT; } safe_strcpy(user->name, name, sizeof(user->name)); safe_strcpy(user->passwd, passwd, sizeof(user->passwd)); db->forced_user = user; return user; } /* find an existing database */ PgDatabase *find_database(const char *name) { struct List *item, *tmp; PgDatabase *db; statlist_for_each(item, &database_list) { db = container_of(item, PgDatabase, head); if (strcmp(db->name, name) == 0) return db; } /* also trying to find in idle autodatabases list */ statlist_for_each_safe(item, &autodatabase_idle_list, tmp) { db = container_of(item, PgDatabase, head); if (strcmp(db->name, name) == 0) { db->inactive_time = 0; statlist_remove(&autodatabase_idle_list, &db->head); put_in_order(&db->head, &database_list, cmp_database); return db; } } return NULL; } /* find existing user */ PgUser *find_user(const char *name) { PgUser *user = NULL; struct AANode *node; node = aatree_search(&user_tree, (uintptr_t)name); user = node ? container_of(node, PgUser, tree_node) : NULL; return user; } /* create new pool object */ static PgPool *new_pool(PgDatabase *db, PgUser *user) { PgPool *pool; pool = slab_alloc(pool_cache); if (!pool) return NULL; list_init(&pool->head); list_init(&pool->map_head); pool->user = user; pool->db = db; statlist_init(&pool->active_client_list, "active_client_list"); statlist_init(&pool->waiting_client_list, "waiting_client_list"); statlist_init(&pool->active_server_list, "active_server_list"); statlist_init(&pool->idle_server_list, "idle_server_list"); statlist_init(&pool->tested_server_list, "tested_server_list"); statlist_init(&pool->used_server_list, "used_server_list"); statlist_init(&pool->new_server_list, "new_server_list"); statlist_init(&pool->cancel_req_list, "cancel_req_list"); list_append(&user->pool_list, &pool->map_head); /* keep pools in db/user order to make stats faster */ put_in_order(&pool->head, &pool_list, cmp_pool); return pool; } /* find pool object, create if needed */ PgPool *get_pool(PgDatabase *db, PgUser *user) { struct List *item; PgPool *pool; if (!db || !user) return NULL; list_for_each(item, &user->pool_list) { pool = container_of(item, PgPool, map_head); if (pool->db == db) return pool; } return new_pool(db, user); } /* deactivate socket and put into wait queue */ static void pause_client(PgSocket *client) { Assert(client->state == CL_ACTIVE || client->state == CL_LOGIN); slog_debug(client, "pause_client"); change_client_state(client, CL_WAITING); if (!sbuf_pause(&client->sbuf)) disconnect_client(client, true, "pause failed"); } /* wake client from wait */ void activate_client(PgSocket *client) { Assert(client->state == CL_WAITING || client->state == CL_WAITING_LOGIN); slog_debug(client, "activate_client"); change_client_state(client, CL_ACTIVE); sbuf_continue(&client->sbuf); } /* * Don't let clients queue at all, if there is no working server connection. * * It must still allow following cases: * - empty pool on startup * - idle pool where all servers are removed * * Current assumptions: * - old server connections will be dropped by query_timeout * - new server connections fail due to server_connect_timeout, or other failure * * So here we drop client if all server connections have been dropped * and new one's fail. */ bool check_fast_fail(PgSocket *client) { int cnt; PgPool *pool = client->pool; /* reject if no servers and last connect failed */ if (!pool->last_connect_failed) return true; cnt = pool_server_count(pool) - statlist_count(&pool->new_server_list); if (cnt) return true; disconnect_client(client, true, "pgbouncer cannot connect to server"); /* usual relaunch won't work, as there are no waiting clients */ launch_new_connection(pool); return false; } /* link if found, otherwise put into wait queue */ bool find_server(PgSocket *client) { PgPool *pool = client->pool; PgSocket *server; bool res; bool varchange = false; Assert(client->state == CL_ACTIVE || client->state == CL_LOGIN); if (client->link) return true; /* try to get idle server, if allowed */ if (cf_pause_mode == P_PAUSE || pool->db->db_paused) { server = NULL; } else { while (1) { server = first_socket(&pool->idle_server_list); if (!server) { break; } else if (server->close_needed) { disconnect_server(server, true, "obsolete connection"); } else if (!server->ready) { disconnect_server(server, true, "idle server got dirty"); } else { break; } } if (!server && !check_fast_fail(client)) return false; } Assert(!server || server->state == SV_IDLE); /* send var changes */ if (server) { res = varcache_apply(server, client, &varchange); if (!res) { disconnect_server(server, true, "var change failed"); server = NULL; } } /* link or send to waiters list */ if (server) { client->link = server; server->link = client; change_server_state(server, SV_ACTIVE); if (varchange) { server->setting_vars = 1; server->ready = 0; res = false; /* don't process client data yet */ if (!sbuf_pause(&client->sbuf)) disconnect_client(client, true, "pause failed"); } else { res = true; } } else { pause_client(client); res = false; } return res; } /* pick waiting client */ static bool reuse_on_release(PgSocket *server) { bool res = true; PgPool *pool = server->pool; PgSocket *client = first_socket(&pool->waiting_client_list); if (client) { activate_client(client); /* * As the activate_client() does full read loop, * then it may happen that linked client closing * causes server closing. Report it. */ if (server->state == SV_FREE || server->state == SV_JUSTFREE) res = false; } return res; } /* send reset query */ static bool reset_on_release(PgSocket *server) { bool res; Assert(server->state == SV_TESTED); slog_debug(server, "Resetting: %s", cf_server_reset_query); SEND_generic(res, server, 'Q', "s", cf_server_reset_query); if (!res) disconnect_server(server, false, "reset query failed"); return res; } static bool life_over(PgSocket *server) { PgPool *pool = server->pool; usec_t lifetime_kill_gap = 0; usec_t now = get_cached_time(); usec_t age = now - server->connect_time; usec_t last_kill = now - pool->last_lifetime_disconnect; if (age < cf_server_lifetime) return false; if (pool->db->pool_size > 0) lifetime_kill_gap = cf_server_lifetime / pool->db->pool_size; if (last_kill >= lifetime_kill_gap) return true; return false; } /* connecting/active -> idle, unlink if needed */ bool release_server(PgSocket *server) { PgPool *pool = server->pool; SocketState newstate = SV_IDLE; Assert(server->ready); /* remove from old list */ switch (server->state) { case SV_ACTIVE: server->link->link = NULL; server->link = NULL; if (*cf_server_reset_query && (cf_server_reset_query_always || pool_pool_mode(pool) == POOL_SESSION)) { /* notify reset is required */ newstate = SV_TESTED; } else if (cf_server_check_delay == 0 && *cf_server_check_query) { /* * deprecated: before reset_query, the check_delay = 0 * was used to get same effect. This if() can be removed * after couple of releases. */ newstate = SV_USED; } case SV_USED: case SV_TESTED: break; case SV_LOGIN: pool->last_connect_failed = 0; break; default: fatal("bad server state in release_server (%d)", server->state); } /* enforce lifetime immediately on release */ if (server->state != SV_LOGIN && life_over(server)) { disconnect_server(server, true, "server_lifetime"); pool->last_lifetime_disconnect = get_cached_time(); return false; } /* enforce close request */ if (server->close_needed) { disconnect_server(server, true, "close_needed"); return false; } Assert(server->link == NULL); slog_noise(server, "release_server: new state=%d", newstate); change_server_state(server, newstate); if (newstate == SV_IDLE) { /* immediately process waiters, to give fair chance */ return reuse_on_release(server); } else if (newstate == SV_TESTED) { return reset_on_release(server); } return true; } /* drop server connection */ void disconnect_server(PgSocket *server, bool notify, const char *reason, ...) { PgPool *pool = server->pool; PgSocket *client; static const uint8_t pkt_term[] = {'X', 0,0,0,4}; int send_term = 1; usec_t now = get_cached_time(); char buf[128]; va_list ap; va_start(ap, reason); vsnprintf(buf, sizeof(buf), reason, ap); va_end(ap); reason = buf; if (cf_log_disconnections) slog_info(server, "closing because: %s (age=%" PRIu64 ")", reason, (now - server->connect_time) / USEC); switch (server->state) { case SV_ACTIVE: client = server->link; if (client) { client->link = NULL; server->link = NULL; disconnect_client(client, true, "%s", reason); } break; case SV_TESTED: case SV_USED: case SV_IDLE: break; case SV_LOGIN: /* * usually disconnect means problems in startup phase, * except when sending cancel packet */ if (!server->ready) pool->last_connect_failed = 1; else send_term = 0; break; default: fatal("disconnect_server: bad server state (%d)", server->state); } Assert(server->link == NULL); /* notify server and close connection */ if (send_term && notify) { if (!sbuf_answer(&server->sbuf, pkt_term, sizeof(pkt_term))) /* ignore result */ notify = false; } if (server->dns_token) { adns_cancel(adns, server->dns_token); server->dns_token = NULL; } server->pool->db->connection_count--; server->pool->user->connection_count--; change_server_state(server, SV_JUSTFREE); if (!sbuf_close(&server->sbuf)) log_noise("sbuf_close failed, retry later"); } /* drop client connection */ void disconnect_client(PgSocket *client, bool notify, const char *reason, ...) { char buf[128]; va_list ap; usec_t now = get_cached_time(); va_start(ap, reason); vsnprintf(buf, sizeof(buf), reason, ap); va_end(ap); reason = buf; if (cf_log_disconnections) slog_info(client, "closing because: %s (age=%" PRIu64 ")", reason, (now - client->connect_time) / USEC); switch (client->state) { case CL_ACTIVE: case CL_LOGIN: if (client->link) { PgSocket *server = client->link; /* ->ready may be set before all is sent */ if (server->ready && sbuf_is_empty(&server->sbuf)) { /* retval does not matter here */ release_server(server); } else { server->link = NULL; client->link = NULL; disconnect_server(server, true, "unclean server"); } } case CL_WAITING: case CL_WAITING_LOGIN: case CL_CANCEL: break; default: fatal("bad client state in disconnect_client: %d", client->state); } /* send reason to client */ if (notify && reason && client->state != CL_CANCEL) { /* * don't send Ready pkt here, or client won't notice * closed connection */ send_pooler_error(client, false, reason); } change_client_state(client, CL_JUSTFREE); if (!sbuf_close(&client->sbuf)) log_noise("sbuf_close failed, retry later"); } /* * Connection creation utilities */ static void connect_server(struct PgSocket *server, const struct sockaddr *sa, int salen) { bool res; /* fill remote_addr */ memset(&server->remote_addr, 0, sizeof(server->remote_addr)); if (sa->sa_family == AF_UNIX) { pga_set(&server->remote_addr, AF_UNIX, server->pool->db->port); } else { pga_copy(&server->remote_addr, sa); } slog_debug(server, "launching new connection to server"); /* start connecting */ res = sbuf_connect(&server->sbuf, sa, salen, cf_server_connect_timeout / USEC); if (!res) log_noise("failed to launch new connection"); } static void dns_callback(void *arg, const struct sockaddr *sa, int salen) { struct PgSocket *server = arg; struct PgDatabase *db = server->pool->db; struct sockaddr_in sa_in; struct sockaddr_in6 sa_in6; server->dns_token = NULL; if (!sa) { disconnect_server(server, true, "server dns lookup failed"); return; } else if (sa->sa_family == AF_INET) { char buf[64]; memcpy(&sa_in, sa, sizeof(sa_in)); sa_in.sin_port = htons(db->port); sa = (struct sockaddr *)&sa_in; salen = sizeof(sa_in); slog_debug(server, "dns_callback: inet4: %s", sa2str(sa, buf, sizeof(buf))); } else if (sa->sa_family == AF_INET6) { char buf[64]; memcpy(&sa_in6, sa, sizeof(sa_in6)); sa_in6.sin6_port = htons(db->port); sa = (struct sockaddr *)&sa_in6; salen = sizeof(sa_in6); slog_debug(server, "dns_callback: inet6: %s", sa2str(sa, buf, sizeof(buf))); } else { disconnect_server(server, true, "unknown address family: %d", sa->sa_family); return; } connect_server(server, sa, salen); } static void dns_connect(struct PgSocket *server) { struct sockaddr_un sa_un; struct sockaddr_in sa_in; struct sockaddr_in6 sa_in6; struct sockaddr *sa; struct PgDatabase *db = server->pool->db; const char *host = db->host; const char *unix_dir; int sa_len; int res; if (!host || host[0] == '/') { slog_noise(server, "unix socket: %s", sa_un.sun_path); memset(&sa_un, 0, sizeof(sa_un)); sa_un.sun_family = AF_UNIX; unix_dir = host ? host : cf_unix_socket_dir; if (!unix_dir || !*unix_dir) { log_error("Unix socket dir not configured: %s", db->name); disconnect_server(server, false, "cannot connect"); return; } snprintf(sa_un.sun_path, sizeof(sa_un.sun_path), "%s/.s.PGSQL.%d", unix_dir, db->port); sa = (struct sockaddr *)&sa_un; sa_len = sizeof(sa_un); res = 1; } else if (strchr(host, ':')) { /* assume IPv6 address on any : in addr */ slog_noise(server, "inet6 socket: %s", db->host); memset(&sa_in6, 0, sizeof(sa_in6)); sa_in6.sin6_family = AF_INET6; res = inet_pton(AF_INET6, db->host, &sa_in6.sin6_addr); sa_in6.sin6_port = htons(db->port); sa = (struct sockaddr *)&sa_in6; sa_len = sizeof(sa_in6); } else { /* else try IPv4 */ slog_noise(server, "inet socket: %s", db->host); memset(&sa_in, 0, sizeof(sa_in)); sa_in.sin_family = AF_INET; res = inet_pton(AF_INET, db->host, &sa_in.sin_addr); sa_in.sin_port = htons(db->port); sa = (struct sockaddr *)&sa_in; sa_len = sizeof(sa_in); } /* if simple parse failed, use DNS */ if (res != 1) { struct DNSToken *tk; slog_noise(server, "dns socket: %s", db->host); /* launch dns lookup */ tk = adns_resolve(adns, db->host, dns_callback, server); if (tk) server->dns_token = tk; return; } connect_server(server, sa, sa_len); } PgSocket *compare_connections_by_time(PgSocket *lhs, PgSocket *rhs) { if (!lhs) return rhs; if (!rhs) return lhs; return lhs->request_time < rhs->request_time ? lhs : rhs; } /* evict the single most idle connection from among all pools to make room in the db */ bool evict_connection(PgDatabase *db) { struct List *item; PgPool *pool; PgSocket *oldest_connection = NULL; statlist_for_each(item, &pool_list) { pool = container_of(item, PgPool, head); if (pool->db != db) continue; oldest_connection = compare_connections_by_time(oldest_connection, last_socket(&pool->idle_server_list)); /* only evict testing connections if nobody's waiting */ if (statlist_empty(&pool->waiting_client_list)) { oldest_connection = compare_connections_by_time(oldest_connection, last_socket(&pool->used_server_list)); oldest_connection = compare_connections_by_time(oldest_connection, last_socket(&pool->tested_server_list)); } } if (oldest_connection) { disconnect_server(oldest_connection, true, "evicted"); return true; } return false; } /* evict the single most idle connection from among all pools to make room in the user */ bool evict_user_connection(PgUser *user) { struct List *item; PgPool *pool; PgSocket *oldest_connection = NULL; statlist_for_each(item, &pool_list) { pool = container_of(item, PgPool, head); if (pool->user != user) continue; oldest_connection = compare_connections_by_time(oldest_connection, last_socket(&pool->idle_server_list)); /* only evict testing connections if nobody's waiting */ if (statlist_empty(&pool->waiting_client_list)) { oldest_connection = compare_connections_by_time(oldest_connection, last_socket(&pool->used_server_list)); oldest_connection = compare_connections_by_time(oldest_connection, last_socket(&pool->tested_server_list)); } } if (oldest_connection) { disconnect_server(oldest_connection, true, "evicted"); return true; } return false; } /* the pool needs new connection, if possible */ void launch_new_connection(PgPool *pool) { PgSocket *server; int total; /* allow only small number of connection attempts at a time */ if (!statlist_empty(&pool->new_server_list)) { log_debug("launch_new_connection: already progress"); return; } /* if server bounces, don't retry too fast */ if (pool->last_connect_failed) { usec_t now = get_cached_time(); if (now - pool->last_connect_time < cf_server_login_retry) { log_debug("launch_new_connection: last failed, wait"); return; } } /* is it allowed to add servers? */ total = pool_server_count(pool); if (total >= pool->db->pool_size && pool->welcome_msg_ready) { /* should we use reserve pool? */ if (cf_res_pool_timeout && pool->db->res_pool_size) { usec_t now = get_cached_time(); PgSocket *c = first_socket(&pool->waiting_client_list); if (c && (now - c->request_time) >= cf_res_pool_timeout) { if (total < pool->db->pool_size + pool->db->res_pool_size) { slog_warning(c, "Taking connection from reserve_pool"); goto allow_new; } } } log_debug("launch_new_connection: pool full (%d >= %d)", total, pool->db->pool_size); return; } allow_new: total = database_max_connections(pool->db); if (total > 0) { /* try to evict unused connections first */ while (pool->db->connection_count >= total) { if (!evict_connection(pool->db)) { break; } } if (pool->db->connection_count >= total) { log_debug("launch_new_connection: database full (%d >= %d)", pool->db->connection_count, total); return; } } total = user_max_connections(pool->user); if (total > 0) { /* try to evict unused connection first */ while (pool->user->connection_count >= total) { if (!evict_user_connection(pool->user)) { break; } } if (pool->user->connection_count >= total) { log_debug("launch_new_connection: user full (%d >= %d)", pool->user->connection_count, total); return; } } /* get free conn object */ server = slab_alloc(server_cache); if (!server) { log_debug("launch_new_connection: no memory"); return; } /* initialize it */ server->pool = pool; server->auth_user = server->pool->user; server->connect_time = get_cached_time(); pool->last_connect_time = get_cached_time(); change_server_state(server, SV_LOGIN); pool->db->connection_count++; pool->user->connection_count++; dns_connect(server); } /* new client connection attempt */ PgSocket *accept_client(int sock, bool is_unix) { bool res; PgSocket *client; /* get free PgSocket */ client = slab_alloc(client_cache); if (!client) { log_warning("cannot allocate client struct"); safe_close(sock); return NULL; } client->connect_time = client->request_time = get_cached_time(); client->query_start = 0; /* FIXME: take local and remote address from pool_accept() */ fill_remote_addr(client, sock, is_unix); fill_local_addr(client, sock, is_unix); change_client_state(client, CL_LOGIN); res = sbuf_accept(&client->sbuf, sock, is_unix); if (!res) { if (cf_log_connections) slog_debug(client, "failed connection attempt"); return NULL; } return client; } /* send cached parameters to client to pretend being server */ /* client managed to authenticate, send welcome msg and accept queries */ bool finish_client_login(PgSocket *client) { switch (client->state) { case CL_LOGIN: change_client_state(client, CL_ACTIVE); case CL_ACTIVE: break; default: fatal("bad client state"); } /* 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); if (cf_pause_mode == P_NONE) launch_new_connection(client->pool); return false; } client->wait_for_welcome = 0; /* send the message */ if (!welcome_client(client)) return false; slog_debug(client, "logged in"); return true; } /* client->cancel_key has requested client key */ void accept_cancel_request(PgSocket *req) { struct List *pitem, *citem; PgPool *pool = NULL; PgSocket *server = NULL, *client, *main_client = NULL; Assert(req->state == CL_LOGIN); /* find real client this is for */ statlist_for_each(pitem, &pool_list) { pool = container_of(pitem, PgPool, head); statlist_for_each(citem, &pool->active_client_list) { client = container_of(citem, PgSocket, head); if (memcmp(client->cancel_key, req->cancel_key, 8) == 0) { main_client = client; goto found; } } statlist_for_each(citem, &pool->waiting_client_list) { client = container_of(citem, PgSocket, head); if (memcmp(client->cancel_key, req->cancel_key, 8) == 0) { main_client = client; goto found; } } } found: /* wrong key */ if (!main_client) { disconnect_client(req, false, "failed cancel request"); return; } /* not linked client, just drop it then */ if (!main_client->link) { bool res; /* let administrative cancel be handled elsewhere */ if (main_client->pool->db->admin) { disconnect_client(req, false, "cancel request for console client"); admin_handle_cancel(main_client); return; } disconnect_client(req, false, "cancel request for idle client"); /* notify readiness */ SEND_ReadyForQuery(res, main_client); if (!res) disconnect_client(main_client, true, "ReadyForQuery for main_client failed"); return; } /* drop the connection, if fails, retry later in justfree list */ if (!sbuf_close(&req->sbuf)) log_noise("sbuf_close failed, retry later"); /* remember server key */ server = main_client->link; memcpy(req->cancel_key, server->cancel_key, 8); /* attach to target pool */ req->pool = pool; change_client_state(req, CL_CANCEL); /* need fresh connection */ launch_new_connection(pool); } void forward_cancel_request(PgSocket *server) { bool res; PgSocket *req = first_socket(&server->pool->cancel_req_list); Assert(req != NULL && req->state == CL_CANCEL); Assert(server->state == SV_LOGIN); SEND_CancelRequest(res, server, req->cancel_key); if (!res) log_warning("sending cancel request failed: %s", strerror(errno)); change_client_state(req, CL_JUSTFREE); } bool use_client_socket(int fd, PgAddr *addr, const char *dbname, const char *username, uint64_t ckey, int oldfd, int linkfd, const char *client_enc, const char *std_string, const char *datestyle, const char *timezone, const char *password) { PgSocket *client; PktBuf tmp; client = accept_client(fd, pga_is_unix(addr)); if (client == NULL) return false; client->suspended = 1; if (!set_pool(client, dbname, username, password, true)) return false; change_client_state(client, CL_ACTIVE); /* store old cancel key */ pktbuf_static(&tmp, client->cancel_key, 8); pktbuf_put_uint64(&tmp, ckey); /* store old fds */ client->tmp_sk_oldfd = oldfd; client->tmp_sk_linkfd = linkfd; varcache_set(&client->vars, "client_encoding", client_enc); varcache_set(&client->vars, "standard_conforming_strings", std_string); varcache_set(&client->vars, "datestyle", datestyle); varcache_set(&client->vars, "timezone", timezone); return true; } bool use_server_socket(int fd, PgAddr *addr, const char *dbname, const char *username, uint64_t ckey, int oldfd, int linkfd, const char *client_enc, const char *std_string, const char *datestyle, const char *timezone, const char *password) { PgDatabase *db = find_database(dbname); PgUser *user; PgPool *pool; PgSocket *server; PktBuf tmp; bool res; /* if the database not found, it's an auto database -> registering... */ if (!db) { db = register_auto_database(dbname); if (!db) return true; } if (db->forced_user) { user = db->forced_user; } else { user = find_user(username); } if (!user && db->auth_user) user = add_db_user(db, username, password); pool = get_pool(db, user); if (!pool) return false; server = slab_alloc(server_cache); if (!server) return false; res = sbuf_accept(&server->sbuf, fd, pga_is_unix(addr)); if (!res) return false; db->connection_count++; server->suspended = 1; server->pool = pool; server->auth_user = user; server->connect_time = server->request_time = get_cached_time(); server->query_start = 0; fill_remote_addr(server, fd, pga_is_unix(addr)); fill_local_addr(server, fd, pga_is_unix(addr)); if (linkfd) { server->ready = 0; change_server_state(server, SV_ACTIVE); } else { server->ready = 1; change_server_state(server, SV_IDLE); } /* store old cancel key */ pktbuf_static(&tmp, server->cancel_key, 8); pktbuf_put_uint64(&tmp, ckey); /* store old fds */ server->tmp_sk_oldfd = oldfd; server->tmp_sk_linkfd = linkfd; varcache_set(&server->vars, "client_encoding", client_enc); varcache_set(&server->vars, "standard_conforming_strings", std_string); varcache_set(&server->vars, "datestyle", datestyle); varcache_set(&server->vars, "timezone", timezone); return true; } void for_each_server(PgPool *pool, void (*func)(PgSocket *sk)) { struct List *item; statlist_for_each(item, &pool->idle_server_list) func(container_of(item, PgSocket, head)); statlist_for_each(item, &pool->used_server_list) func(container_of(item, PgSocket, head)); statlist_for_each(item, &pool->tested_server_list) func(container_of(item, PgSocket, head)); statlist_for_each(item, &pool->active_server_list) func(container_of(item, PgSocket, head)); statlist_for_each(item, &pool->new_server_list) func(container_of(item, PgSocket, head)); } static void for_each_server_filtered(PgPool *pool, void (*func)(PgSocket *sk), bool (*filter)(PgSocket *sk, void *arg), void *filter_arg) { struct List *item; PgSocket *sk; statlist_for_each(item, &pool->idle_server_list) { sk = container_of(item, PgSocket, head); if (filter(sk, filter_arg)) func(sk); } statlist_for_each(item, &pool->used_server_list) { sk = container_of(item, PgSocket, head); if (filter(sk, filter_arg)) func(sk); } statlist_for_each(item, &pool->tested_server_list) { sk = container_of(item, PgSocket, head); if (filter(sk, filter_arg)) func(sk); } statlist_for_each(item, &pool->active_server_list) { sk = container_of(item, PgSocket, head); if (filter(sk, filter_arg)) func(sk); } statlist_for_each(item, &pool->new_server_list) { sk = container_of(item, PgSocket, head); if (filter(sk, filter_arg)) func(sk); } } static void tag_dirty(PgSocket *sk) { sk->close_needed = 1; } static void tag_pool_dirty(PgPool *pool) { struct List *item, *tmp; struct PgSocket *server; /* reset welcome msg */ if (pool->welcome_msg) { pktbuf_free(pool->welcome_msg); pool->welcome_msg = NULL; } pool->welcome_msg_ready = 0; /* drop all existing servers ASAP */ for_each_server(pool, tag_dirty); /* drop servers login phase immediately */ statlist_for_each_safe(item, &pool->new_server_list, tmp) { server = container_of(item, PgSocket, head); disconnect_server(server, true, "connect string changed"); } } void tag_database_dirty(PgDatabase *db) { struct List *item; PgPool *pool; statlist_for_each(item, &pool_list) { pool = container_of(item, PgPool, head); if (pool->db == db) tag_pool_dirty(pool); } } void tag_autodb_dirty(void) { struct List *item, *tmp; PgDatabase *db; PgPool *pool; /* * reload databases. */ statlist_for_each(item, &database_list) { db = container_of(item, PgDatabase, head); if (db->db_auto) register_auto_database(db->name); } statlist_for_each_safe(item, &autodatabase_idle_list, tmp) { db = container_of(item, PgDatabase, head); if (db->db_auto) register_auto_database(db->name); } /* * reload pools */ statlist_for_each(item, &pool_list) { pool = container_of(item, PgPool, head); if (pool->db->db_auto) tag_pool_dirty(pool); } } static bool server_remote_addr_filter(PgSocket *sk, void *arg) { PgAddr *addr = arg; return (pga_cmp_addr(&sk->remote_addr, addr) == 0); } void tag_host_addr_dirty(const char *host, const struct sockaddr *sa) { struct List *item; PgPool *pool; PgAddr addr; memset(&addr, 0, sizeof(addr)); pga_copy(&addr, sa); statlist_for_each(item, &pool_list) { pool = container_of(item, PgPool, head); if (pool->db->host && strcmp(host, pool->db->host) == 0) { for_each_server_filtered(pool, tag_dirty, server_remote_addr_filter, &addr); } } } /* move objects from justfree_* to free_* lists */ void reuse_just_freed_objects(void) { struct List *tmp, *item; PgSocket *sk; bool close_works = true; /* * event_del() may fail because of ENOMEM for event handlers * that need only changes sent to kernel on each loop. * * Keep open sbufs in justfree lists until successful. */ statlist_for_each_safe(item, &justfree_client_list, tmp) { sk = container_of(item, PgSocket, head); if (sbuf_is_closed(&sk->sbuf)) { change_client_state(sk, CL_FREE); } else if (close_works) { close_works = sbuf_close(&sk->sbuf); } } statlist_for_each_safe(item, &justfree_server_list, tmp) { sk = container_of(item, PgSocket, head); if (sbuf_is_closed(&sk->sbuf)) { change_server_state(sk, SV_FREE); } else if (close_works) { close_works = sbuf_close(&sk->sbuf); } } } pgbouncer-1.7/COPYRIGHT0000664000175000017500000000146312060213747011571 00000000000000PgBouncer - Lightweight connection pooler for PostgreSQL. Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. pgbouncer-1.7/win32/0000775000175000017500000000000012635051616011317 500000000000000pgbouncer-1.7/win32/pgbevent.c0000664000175000017500000000512512060213747013215 00000000000000/*------------------------------------------------------------------------- * pgbevent.c * Defines the entry point for pgbevent dll. * The DLL defines event source for pgbouncer tools *------------------------------------------------------------------------- */ #define WIN32_LEAN_AND_MEAN #include #include #include #define APP_KEY "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\pgbouncer" /* Global variables */ static HANDLE g_module = NULL; /* hModule of DLL */ /* Prototypes */ STDAPI DllRegisterServer(void); STDAPI DllUnregisterServer(void); BOOL WINAPI DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved); /* * DllRegisterServer --- Instructs DLL to create its registry entries */ STDAPI DllRegisterServer(void) { HKEY key; DWORD data; char buffer[_MAX_PATH]; /* Set the name of DLL full path name. */ if (!GetModuleFileName((HMODULE)g_module, buffer, sizeof(buffer))) { MessageBox(NULL, "Could not retrieve DLL filename", "pgbouncer error", MB_OK | MB_ICONSTOP); return SELFREG_E_TYPELIB; } /* * Add our source name as a subkey under the Application key in * the EventLog registry key. */ if (RegCreateKey(HKEY_LOCAL_MACHINE, APP_KEY, &key)) { MessageBox(NULL, "Could not create the registry key.", "pgbouncer error", MB_OK | MB_ICONSTOP); return SELFREG_E_TYPELIB; } /* Add the name to the EventMessageFile subkey. */ if (RegSetValueEx(key, "EventMessageFile", 0, REG_EXPAND_SZ, (LPBYTE)buffer, strlen(buffer) + 1)) { MessageBox(NULL, "Could not set the event message file.", "pgbouncer error", MB_OK | MB_ICONSTOP); return SELFREG_E_TYPELIB; } /* Set the supported event types in the TypesSupported subkey. */ data = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE; if (RegSetValueEx(key, "TypesSupported", 0, REG_DWORD, (LPBYTE)&data, sizeof(DWORD))) { MessageBox(NULL, "Could not set the supported types.", "pgbouncer error", MB_OK | MB_ICONSTOP); return SELFREG_E_TYPELIB; } RegCloseKey(key); return S_OK; } /* * DllUnregisterServer --- Instructs DLL to remove only those entries created through DllRegisterServer */ STDAPI DllUnregisterServer(void) { if (RegDeleteKey(HKEY_LOCAL_MACHINE, APP_KEY)) { MessageBox(NULL, "Could not delete the registry key.", "pgbouncer error", MB_OK | MB_ICONSTOP); return SELFREG_E_TYPELIB; } return S_OK; } /* * DllMain --- is an optional entry point into a DLL. */ BOOL WINAPI DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { if (ul_reason_for_call == DLL_PROCESS_ATTACH) g_module = hModule; return TRUE; } pgbouncer-1.7/win32/MSG00001.bin0000664000175000017500000000004012060213747012727 00000000000000%1 pgbouncer-1.7/win32/win32support.c0000664000175000017500000002016312511203334013771 00000000000000/*------------------------------------------------------------------------- * win32service.c * * Windows service integration and eventlog * * Copyright (c) 2005, PostgreSQL Global Development Group * Authors: Magnus Hagander, Hiroshi Saito, Marko Kreen *------------------------------------------------------------------------- */ #include "bouncer.h" #if defined(UNICODE) || defined(_UNICODE) #error This code does not support wide characters. #endif /* Globals for service control */ static SERVICE_STATUS_HANDLE hStatus = 0; static SERVICE_STATUS svcStatus = { .dwServiceType = SERVICE_WIN32_OWN_PROCESS, .dwControlsAccepted = 0, .dwWin32ExitCode = NO_ERROR, .dwCheckPoint = 0, .dwWaitHint = 0, .dwCurrentState = SERVICE_START_PENDING, }; /* Event source name for ReportEvent. * * Also used as placeholder for service handling API's, but it is ignored * because our service is defined as WIN32_OWN_PROCESS. */ static char *servicename = "pgbouncer"; static char *service_username = NULL; static char *service_password = NULL; static char *serviceDescription = "Lightweight connection pooler for PostgreSQL."; /* custom help string for win32 exe */ static const char usage_str[] = "Usage: %s [OPTION]... config.ini\n" " -q No console messages\n" " -v Increase verbosity\n" " -V Show version\n" " -h Show this help screen and exit\n" "Windows service registration:\n" " --regservice config.ini [-U username [-P password]]\n" " --unregservice config.ini\n" ""; static void usage(int err, char *exe) { printf(usage_str, basename(exe)); exit(err); } static int exec_real_main(int argc, char *argv[]) { int i, j; /* win32 stdio seems to be fully buffered by default */ setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stderr, NULL, _IONBF, 0); /* check if regular arguments are in allowed list */ for (i = 1; i < argc; i++) { char *p = argv[i]; if (p[0] != '-') continue; for (j = 1; p[j]; j++) { if (!strchr("qvhV", p[j])) usage(1, argv[0]); if (p[j] == 'h') usage(0, argv[0]); } } /* call actual main() */ return real_main(argc, argv); } /* Set the current service status */ static void win32_setservicestatus(DWORD state) { svcStatus.dwCurrentState = state; switch (state) { case SERVICE_START_PENDING: case SERVICE_STOP_PENDING: svcStatus.dwControlsAccepted = 0; svcStatus.dwWaitHint = 5000; break; default: svcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; svcStatus.dwWaitHint = 0; } SetServiceStatus(hStatus, &svcStatus); } /* * Handle any events sent by the service control manager * NOTE! Events are sent on a different thread! And it's * not a pthreads thread, so avoid calling anything that * may use pthreads - like pgbouncer_log() */ static void WINAPI win32_servicehandler(DWORD request) { switch (request) { case SERVICE_CONTROL_STOP: case SERVICE_CONTROL_SHUTDOWN: win32_setservicestatus(SERVICE_STOP_PENDING); cf_shutdown = 2; break; case SERVICE_CONTROL_INTERROGATE: SetServiceStatus(hStatus, &svcStatus); break; } } /* notify control thread about stop */ static void win32_service_cleanup(void) { if (hStatus) win32_setservicestatus(SERVICE_STOPPED); hStatus = 0; /* may get called twice from atexit */ } /* * Entrypoint for actual service work. * * Service is set-up and then actual main() is called. */ static void WINAPI win32_servicemain(DWORD argc, LPSTR *argv) { int new_argc = 2; char *new_argv[] = { servicename, cf_config_file, NULL }; /* register control request handler */ hStatus = RegisterServiceCtrlHandler(servicename, win32_servicehandler); if (hStatus == 0) { fatal("could not connect to service control handler: %s", strerror(GetLastError())); exit(1); } /* Tell SCM we are running before we make any API calls */ win32_setservicestatus(SERVICE_RUNNING); /* register with system atexit(), in case somebody calls exit() */ atexit(win32_service_cleanup); /* Execute actual main() */ exec_real_main(new_argc, new_argv); win32_service_cleanup(); } /* Start running as a service */ static void win32_servicestart(void) { SERVICE_TABLE_ENTRY st[] = { {servicename, win32_servicemain}, {NULL, NULL} }; if (StartServiceCtrlDispatcher(st) == 0) { fprintf(stderr, "could not start service control dispatcher: %s\n", strerror(GetLastError())); exit(1); } } /* Open Service Control Manager */ static SC_HANDLE openSCM(void) { SC_HANDLE manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); if (!manager) { fprintf(stderr, "Failed to open service control manager: %s\n", strerror(GetLastError())); exit(1); } return manager; } /* Full path to current config file. */ static const char *get_config_fullpath(void) { DWORD r; static char buf[PATH_MAX]; r = GetFullPathName(cf_config_file, sizeof(buf), buf, NULL); if (r == 0 || r >= sizeof(buf)) { fprintf(stderr, "Failed to get full pathname for '%s': %s\n", cf_config_file, strerror(GetLastError())); exit(1); } return buf; } /* Register a service with the specified name with the local service control manager */ static void RegisterService(void) { char self[1024]; char cmdline[2048]; const char *config_fn = get_config_fullpath(); SC_HANDLE manager; SC_HANDLE service; SERVICE_DESCRIPTION sd; DWORD r; r = GetModuleFileName(NULL, self, sizeof(self)); if (!r || r >= sizeof(self)) { fprintf(stderr, "Failed to determine path name: %s\n", strerror(GetLastError())); exit(1); } snprintf(cmdline, sizeof(cmdline), "%s --service \"%s\"", self, config_fn); manager = openSCM(); service = CreateService(manager, cf_jobname, cf_jobname, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, cmdline, NULL, NULL, "RPCSS\0", service_username, service_password); if (!service) { fprintf(stderr, "Failed to create service: %s\n", strerror(GetLastError())); exit(1); } /* explain the service purpose */ sd.lpDescription = serviceDescription; ChangeServiceConfig2(service, SERVICE_CONFIG_DESCRIPTION, &sd); CloseServiceHandle(service); CloseServiceHandle(manager); printf("Service registered.\n"); if (service_username == NULL) { printf("\nWARNING! Service is registered to run as Local System. You are\n"); printf("encouraged to change this to a low privilege account to increase\n"); printf("system security. (Eg. NT AUTHORITY\\Local Service)\n"); } } /* Remove a service with the specified name from the local service control manager */ static void UnRegisterService(void) { SC_HANDLE manager; SC_HANDLE service; manager = openSCM(); service = OpenService(manager, cf_jobname, SC_MANAGER_ALL_ACCESS); if (!service) { fprintf(stderr, "Failed to open service: %s\n", strerror(GetLastError())); exit(1); } if (!DeleteService(service)) { fprintf(stderr, "Failed to delete service: %s\n", strerror(GetLastError())); exit(1); } CloseServiceHandle(service); CloseServiceHandle(manager); printf("Service removed.\n"); } /* config loader for service register/unregister */ static void win32_load_config(char *conf) { cf_config_file = conf; init_objects(); load_config(); } /* * Wrapper around actual main() that handles win32 hacks. */ #undef main int main(int argc, char *argv[]) { WSADATA wsaData; /* initialize socket subsystem */ if (WSAStartup(MAKEWORD(2,0), &wsaData)) fatal("Cannot start the network subsystem"); /* service cmdline */ if (argc >= 3) { if (!strcmp(argv[1], "--service") || !strcmp(argv[1], "-service")) { cf_quiet = 1; cf_config_file = argv[2]; win32_servicestart(); return 0; } if (!strcmp(argv[1], "--regservice") || !strcmp(argv[1], "-regservice")) { int i; win32_load_config(argv[2]); for (i = 3; i < argc; i++) { if (!strcmp(argv[i], "-U") && i + 1 < argc) { service_username = argv[++i]; } else if (!strcmp(argv[i], "-P") && i + 1 < argc) { service_password = argv[++i]; } else { printf("unknown arg: %s\n", argv[i]); usage(1, argv[0]); } } RegisterService(); return 0; } if (!strcmp(argv[1], "--unregservice") || !strcmp(argv[1], "-unregservice")) { win32_load_config(argv[2]); UnRegisterService(); return 0; } } return exec_real_main(argc, argv); } pgbouncer-1.7/win32/eventmsg.rc0000664000175000017500000000004712060213747013413 00000000000000LANGUAGE 0x9,0x1 1 11 "MSG00001.bin" pgbouncer-1.7/win32/win32support.h0000664000175000017500000000014012060213747013777 00000000000000 /* redirect main() */ #define main(a,b) real_main(a,b) int real_main(int argc, char *argv[]); pgbouncer-1.7/win32/Makefile0000664000175000017500000000011712060213747012673 00000000000000 # create eventmsg.rc + MSG*.bin eventmsg.rc: eventmsg.mc mc.exe eventmsg.mc pgbouncer-1.7/win32/eventmsg.mc0000664000175000017500000000007512060213747013407 00000000000000MessageId=0 SymbolicName=MSG_PGBOUNCER Language=English %1 . pgbouncer-1.7/README.rst0000664000175000017500000001103612572127367011774 00000000000000 PgBouncer ========= Lightweight connection pooler for PostgreSQL. Homepage https://pgbouncer.github.io Sources, bugtracking https://github.com/pgbouncer/pgbouncer Building --------- PgBouncer depends on few things to get compiled: * `GNU Make`_ 3.81+ * libevent_ 2.0 * (optional) OpenSSL_ 1.0.1 for TLS support. * (optional) `c-ares`_ as alternative to libevent's evdns. .. _GNU Make: https://www.gnu.org/software/make/ .. _libevent: http://libevent.org/ .. _OpenSSL: https://www.openssl.org/ .. _`c-ares`: http://c-ares.haxx.se/ When dependencies are installed just run:: $ ./configure --prefix=/usr/local --with-libevent=libevent-prefix $ make $ make install If you are building from git, or are building for Windows, please see separate build instructions below. DNS lookup support ------------------ Starting from PgBouncer 1.4, it does hostname lookups at connect time instead just once at config load time. This requires proper async DNS implementation. Following list shows supported backends and their probing order: +----------------------------+----------+-----------+------------+----------------+---------------------------------------+ | backend | parallel | EDNS0 (1) | /etc/hosts | SOA lookup (2) | note | +============================+==========+===========+============+================+=======================================+ | c-ares | yes | yes | yes | yes | ipv6+CNAME buggy in <=1.10 | +----------------------------+----------+-----------+------------+----------------+---------------------------------------+ | udns | yes | yes | no | yes | ipv4-only | +----------------------------+----------+-----------+------------+----------------+---------------------------------------+ | evdns, libevent 2.x | yes | no | yes | no | does not check /etc/hosts updates | +----------------------------+----------+-----------+------------+----------------+---------------------------------------+ | getaddrinfo_a, glibc 2.9+ | yes | yes (3) | yes | no | N/A on non-linux | +----------------------------+----------+-----------+------------+----------------+---------------------------------------+ | getaddrinfo, libc | no | yes (3) | yes | no | N/A on win32, requires pthreads | +----------------------------+----------+-----------+------------+----------------+---------------------------------------+ | evdns, libevent 1.x | yes | no | no | no | buggy | +----------------------------+----------+-----------+------------+----------------+---------------------------------------+ 1. EDNS0 is required to have more than 8 addresses behind one hostname. 2. SOA lookup is needed to re-check hostnames on zone serial change 3. To enable EDNS0, add `options edns0` to /etc/resolv.conf `./configure` also has flags `--enable-evdns` and `--disable-evdns` which turn off automatic probing and force use of either `evdns` or `getaddrinfo_a()`. Building from GIT ----------------- Building PgBouncer from GIT requires that you fetch libusual submodule and generate the header and config files before you can run configure:: $ git clone https://github.com/pgbouncer/pgbouncer.git $ cd pgbouncer $ git submodule init $ git submodule update $ ./autogen.sh $ ./configure ... $ make $ make install Additional packages required: autoconf, automake, libtool, autoconf-archive, python-docutils, and pkg-config. Building for WIN32 ------------------ At the moment only build env tested is MINGW32 / MSYS. Cygwin and Visual $ANYTHING are untested. Libevent 2.x is required for DNS hostname lookup. Then do the usual:: $ ./configure ... $ make If cross-compiling from Unix:: $ ./configure --host=i586-mingw32msvc ... Running on WIN32 ---------------- Running from command-line goes as usual, except -d (daemonize), -R (reboot) and -u (switch user) switches will not work. To run pgbouncer as a Windows service, you need to configure `service_name` parameter to set name for service. Then:: $ pgbouncer -regservice config.ini To uninstall service:: $ pgbouncer -unregservice config.ini To use Windows Event Log, set "syslog = 1" in config file. But before you need to register pgbevent.dll:: $ regsvr32 pgbevent.dll To unregister it, do:: $ regsvr32 /u pgbevent.dll pgbouncer-1.7/test/0000775000175000017500000000000012635051616011334 500000000000000pgbouncer-1.7/test/test.ini0000664000175000017500000000577012635046424012746 00000000000000;; database name = connect string [databases] p0 = port=6666 host=127.0.0.1 dbname=p0 user=bouncer pool_size=2 p1 = port=6666 host=127.0.0.1 dbname=p0 user=bouncer p2 = port=6668 host=127.0.0.1 dbname=p2 user=bouncer authdb = port=6666 host=127.0.0.1 dbname=p1 auth_user=pswcheck ;; Configuation section [pgbouncer] ;;; ;;; Administrative settings ;;; logfile = test.log pidfile = test.pid ;;; ;;; Where to wait for clients ;;; ; ip address or * which means all ip-s listen_addr = 127.0.0.1 listen_port = 6667 unix_socket_dir = /tmp ;;; ;;; Authentication settings ;;; ; any, trust, plain, crypt, md5 auth_type = trust #auth_file = 8.0/main/global/pg_auth auth_file = userlist.txt ;;; ;;; Pooler personality questions ;;; ; When server connection is released back to pool: ; session - after client disconnects ; transaction - after transaction finishes ; statement - after statement finishes pool_mode = statement ; When taking idle server into use, this query is ran first. ; ; Query for session pooling: ; ABORT; RESET ALL; SET SESSION AUTHORIZATION DEFAULT ; Query for statement/transaction pooling: ; SELECT 1 ; Empty query disables the functionality server_check_query = select 1 ; If server was used more recently that this many seconds ago, ; skip the check query. If 0, the check query is always ran. server_check_delay = 10 ;;; ;;; Connection limits ;;; ; total number of clients that can connect max_client_conn = 10 default_pool_size = 5 ;;; ;;; Timeouts ;;; ; Close server connection if its been connected longer. server_lifetime = 120 ; Close server connection if its not been used in this time. ; Allows to clean unneccessary connections from pool after peak. server_idle_timeout = 60 ; Cancel connection attepmt if server does not answer takes longer. server_connect_timeout = 15 ; If server login failed (server_connect_timeout or auth failure) ; then wait this many second. server_login_retry = 15 ; Dangerous. Server connection is closed if query does not return ; in this time. Should be used to survive network problems, ; _not_ as statement_timeout. (default: 0) query_timeout = 0 ; Dangerous. Client connection is closed if no activity in this time. ; Should be used to survive network problems. (default: 0) client_idle_timeout = 0 ;;; ;;; Low-level tuning options ;;; ; buffer for streaming packets ;pkt_buf = 4096 ;;; ;;; networking options, for info: man 7 tcp ;;; ; linux: notify program about new connection only if there ; is also data received. (Seconds to wait.) tcp_defer_accept = 0 ;; following options are reloadable, but apply only to ;; new connections. ; in-kernel buffer size (linux default: 4096) tcp_socket_buffer = 0 ; whether tcp keepalive should be turned on (0/1) tcp_keepalive = 0 ;; following options are linux-specific. ;; they also require tcp_keepalive=1 ; count of keepaliva packets tcp_keepcnt = 0 ; how long the connection can be idle, ; before sending keepalive packets tcp_keepidle = 0 ; The time between individual keepalive probes. tcp_keepintvl = 0 pgbouncer-1.7/test/hba_test.rules0000664000175000017500000000252012572127367014127 00000000000000 # METHOD: trust, reject, md5, password, peer, cert # local DATABASE USER METHOD [OPTIONS] # host DATABASE USER ADDRESS METHOD [OPTIONS] # hostssl DATABASE USER ADDRESS METHOD [OPTIONS] # hostnossl DATABASE USER ADDRESS METHOD [OPTIONS] # ws # z # testing local dbp all peer local all userp password local dbz userz trust local all all md5 hostssl all all 10.0.0.0/8 cert hostnossl all all 11.0.0.0/8 md5 host all all 127.0.0.0 255.255.255.0 password host db1 all 0.0.0.0/0 md5 host all user1 15.0.0.0/8 md5 host tmp1,all user1,user2 , user3 16.0.0.0/8 md5 host tmp2,all u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u2 17.0.0.0/8 md5 host d1,d2,d3,d4,d5,d6,d7,d8,d9,d10,d11 t18user 18.0.0.0/8 trust # comment host "all" "all" 19.0.0.0/8 cert host "q1""q2" "a , b" 19.0.0.0/8 cert # mask host mdb muser 199.199.199.199/32 cert host mdb2 muser 128.0.0.0/9 trust host mdb2 muser 128.0.0.0/8 md5 host mdb2 muser 128.0.0.0/7 cert host mdb2 muser 128.0.0.0/6 password host mdb2 muser 128.0.0.0/5 cert host mdb2 muser 128.0.0.0/4 trust host mdb2 muser 128.0.0.0/3 md5 host mdb2 muser 128.0.0.0/2 password host mdb2 muser 128.0.0.0/1 cert # ipv6 host mdb muser ff11::0/16 md5 host mdb muser ff20::/12 md5 host mdb muser ::1/128 trust pgbouncer-1.7/test/hba_test.c0000664000175000017500000000421612572127367013223 00000000000000 #include "bouncer.h" #include #include #include #include #include #include #include int cf_tcp_keepcnt; int cf_tcp_keepintvl; int cf_tcp_keepidle; int cf_tcp_keepalive; int cf_tcp_socket_buffer; int cf_listen_port; static const char *method2string[] = { "trust", "x1", "x2", "password", "crypt", "md5", "creds", "cert", "peer", "hba", "reject", }; static char *get_token(char **ln_p) { char *ln = *ln_p, *tok, *end; while (*ln && *ln == '\t') ln++; tok = ln; while (*ln && *ln != '\t') ln++; end = ln; while (*ln && *ln == '\t') ln++; *ln_p = ln; if (tok == end) return NULL; *end = 0; return tok; } static int hba_test_eval(struct HBA *hba, char *ln, int linenr) { const char *addr=NULL, *user=NULL, *db=NULL, *tls=NULL, *exp=NULL; PgAddr pgaddr; int res; if (ln[0] == '#') return 0; exp = get_token(&ln); db = get_token(&ln); user = get_token(&ln); addr = get_token(&ln); tls = get_token(&ln); if (!exp) return 0; if (!db || !user) die("hbatest: invalid line #%d", linenr); if (!pga_pton(&pgaddr, addr, 9999)) die("hbatest: invalid addr on line #%d", linenr); res = hba_eval(hba, &pgaddr, !!tls, db, user); if (strcmp(method2string[res], exp) == 0) { res = 0; } else { log_warning("FAIL on line %d: expected '%s' got '%s' - user=%s db=%s addr=%s", linenr, exp, method2string[res], user, db, addr); res = 1; } return res; } static void hba_test(void) { struct HBA *hba; FILE *f; char *ln = NULL; size_t lnbuf = 0; ssize_t len; int linenr; int nfailed = 0; hba = hba_load_rules("hba_test.rules"); if (!hba) die("hbatest: did not find config"); f = fopen("hba_test.eval", "r"); if (!f) die("hbatest: cannot open eval"); for (linenr = 1; ; linenr++) { len = getline(&ln, &lnbuf, f); if (len < 0) break; if (len && ln[len-1] == '\n') ln[len-1] = 0; nfailed += hba_test_eval(hba, ln, linenr); } free(ln); fclose(f); hba_free(hba); if (nfailed) errx(1, "HBA test failures: %d", nfailed); else printf("HBA test OK\n"); } int main(void) { hba_test(); return 0; } pgbouncer-1.7/test/conntest.sh0000775000175000017500000000147112060213747013450 00000000000000#!/bin/sh fw_drop_port() { echo "fw_drop_port" case `uname` in Linux) sudo iptables -A OUTPUT -p tcp --dport $1 -j DROP;; Darwin) sudo ipfw add 100 drop tcp from any to 127.0.0.1 dst-port $1;; *) echo "Unknown OS";; esac } fw_reject_port() { echo "fw_reject_port" case `uname` in Linux) sudo iptables -A OUTPUT -p tcp --dport $1 -j REJECT --reject-with tcp-reset;; Darwin) sudo ipfw add 100 reset tcp from any to 127.0.0.1 dst-port $1;; *) echo "Unknown OS";; esac } fw_reset() { echo "fw_reset" case `uname` in Linux) sudo iptables -F;; Darwin) sudo ipfw del 100;; *) echo "Unknown OS"; exit 1;; esac } port=5432 port=7000 fw_reset while true; do fw_drop_port $port sleep 12 fw_reset sleep 12 fw_reject_port $port sleep 3 fw_reset sleep 6 done pgbouncer-1.7/test/stress.py0000775000175000017500000000473412060213747013161 00000000000000#! /usr/bin/env python import sys, os, re, time, psycopg import threading, thread, random n_thread = 100 longtx = 0 tx_sleep = 0 tx_sleep = 8 conn_data = { 'dbname': 'marko', #'host': '127.0.0.1', 'host': '/tmp', 'port': '6000', 'user': 'marko', #'password': '', 'connect_timeout': '5', } def get_connstr(): tmp = [] for k, v in conn_data.items(): tmp.append(k+'='+v) return " ".join(tmp) class WorkThread(threading.Thread): def __init__(self): threading.Thread.__init__(self) self.setDaemon(True) self.stat_lock = threading.Lock() self.query_cnt = 0 def inc_cnt(self): self.stat_lock.acquire() self.query_cnt += 1 self.stat_lock.release() def fetch_cnt(self): self.stat_lock.acquire() val = self.query_cnt self.query_cnt = 0 self.stat_lock.release() return val def run(self): try: time.sleep(random.random() * 10.0) except: pass while 1: try: self.main_loop() except KeyboardInterrupt: break except SystemExit: break except Exception, d: print d try: time.sleep(5) except: pass def main_loop(self): db = psycopg.connect(get_connstr()) if not longtx: db.autocommit(1) n = 0 while n < 10: self.do_work(db) self.inc_cnt() n += 1 def do_work(self, db): curs = db.cursor() q = "select pg_sleep(%.02f)" % (random.random() * 1) curs.execute(q) time.sleep(tx_sleep * random.random() + 1) if longtx: db.commit() def main(): print "connstr", get_connstr() thread_list = [] while len(thread_list) < n_thread: t = WorkThread() t.start() thread_list.append(t) print "started %d threads" % len(thread_list) last = time.time() while 1: time.sleep(1) now = time.time() dur = now - last if dur >= 5: last = now cnt = 0 for t in thread_list: cnt += t.fetch_cnt() avg = cnt / dur print "avg", avg if __name__ == '__main__': try: main() except SystemExit: pass except KeyboardInterrupt: pass #except Exception, d: # print d pgbouncer-1.7/test/ctest7000.ini0000664000175000017500000000140712060213747013405 00000000000000;; database name = connect string [databases] ; redirect bardb to bazdb on localhost conntest = host=127.0.0.1 port=5432 dbname=marko password=kama ;; Configuation section [pgbouncer] logfile = ctest7000.log pidfile = ctest7000.pid listen_addr = 127.0.0.1 listen_port = 7000 unix_socket_dir = /tmp auth_type = md5 auth_file = userlist.txt admin_users = marko stats_users = stats pool_mode = transaction server_reset_query = reset all server_check_query = select 1 server_check_delay = 2 max_client_conn = 5000 default_pool_size = 20 log_connections = 0 log_disconnections = 0 log_pooler_errors = 0 server_lifetime = 30 server_idle_timeout = 3 server_connect_timeout = 2 server_login_retry = 5 query_timeout = 5 client_idle_timeout = 10 client_login_timeout = 50 pgbouncer-1.7/test/ctest6000.ini0000664000175000017500000000137412060213747013407 00000000000000;; database name = connect string [databases] ; redirect bardb to bazdb on localhost conntest = host=127.0.0.1 port=7000 dbname=conntest ;; Configuation section [pgbouncer] logfile = ctest6000.log pidfile = ctest6000.pid listen_addr = 127.0.0.1 listen_port = 6000 unix_socket_dir = /tmp auth_type = md5 auth_file = userlist.txt admin_users = marko stats_users = stats pool_mode = transaction server_reset_query = reset all server_check_query = select 1 server_check_delay = 2 max_client_conn = 1000 default_pool_size = 20 log_connections = 0 log_disconnections = 0 log_pooler_errors = 0 server_lifetime = 30 server_idle_timeout = 3 server_connect_timeout = 2 server_login_retry = 5 query_timeout = 5 client_idle_timeout = 10 client_login_timeout = 50 pgbouncer-1.7/test/run-conntest.sh0000664000175000017500000000022112060213747014237 00000000000000#! /bin/sh createdb conntest ./pgbouncer -d ctest6000.ini ./pgbouncer -d ctest7000.ini ./asynctest # now run conntest.sh on another console pgbouncer-1.7/test/userlist.txt0000664000175000017500000000017212572127367013676 00000000000000"marko" "kama" "postgres" "asdasd" ;Commented out line should be ignored. "pgbouncer" "fake" "pswcheck" "pgbouncer-check" pgbouncer-1.7/test/test.sh0000775000175000017500000002434312572127367012607 00000000000000#!/bin/bash # Notes: # - uses iptables and -F with some tests, probably not very friendly to your firewall cd $(dirname $0) export PATH=/usr/lib/postgresql/9.4/bin:$PATH export PGDATA=$PWD/pgdata export PGHOST=localhost export PGPORT=6667 export EF_ALLOW_MALLOC_0=1 export LANG=C BOUNCER_LOG=test.log BOUNCER_INI=test.ini BOUNCER_PID=test.pid BOUNCER_PORT=`sed -n '/^listen_port/s/listen_port.*=[^0-9]*//p' $BOUNCER_INI` BOUNCER_EXE="../pgbouncer" LOGDIR=log NC_PORT=6668 PG_PORT=6666 PG_LOG=$LOGDIR/pg.log pgctl() { pg_ctl -o "-p $PG_PORT" -D $PGDATA $@ >>$PG_LOG 2>&1 } ulimit -c unlimited which initdb > /dev/null || { echo "initdb not found, need postgres tools in PATH" exit 1 } # System configuration checks grep -q "^\"${USER}\"" userlist.txt || echo "\"${USER}\" \"01234\"" >> userlist.txt case `uname` in Darwin|OpenBSD) sudo pfctl -a pgbouncer -F all -q 2>&1 | grep -q "pfctl:" && { cat <<-EOF Please enable PF and add the following rule to /etc/pf.conf anchor "pgbouncer/*" EOF exit 1 } ;; esac # System configuration checks SED_ERE_OP='-E' NC_WAIT_OP='-w 5' case `uname` in Linux) SED_ERE_OP='-r' NC_WAIT_OP='-q 5' ;; esac stopit() { test -f "$1" && { kill `cat "$1"`; rm -f "$1"; } } stopit test.pid stopit pgdata/postmaster.pid mkdir -p $LOGDIR rm -f $BOUNCER_LOG $PG_LOG rm -rf $PGDATA if [ ! -d $PGDATA ]; then mkdir $PGDATA initdb >> $PG_LOG 2>&1 sed $SED_ERE_OP -i "/unix_socket_director/s:.*(unix_socket_director.*=).*:\\1 '/tmp':" pgdata/postgresql.conf fi pgctl start sleep 5 echo "Creating databases" psql -p $PG_PORT -l |grep p0 > /dev/null || { psql -p $PG_PORT -c "create user bouncer" template1 createdb -p $PG_PORT p0 createdb -p $PG_PORT p1 createdb -p $PG_PORT p3 } psql -p $PG_PORT -d p0 -c "select * from pg_user" | grep pswcheck > /dev/null || { psql -p $PG_PORT p0 -c "create user pswcheck with superuser createdb password 'pgbouncer-check';" || return 1 psql -p $PG_PORT p0 -c "create user someuser with password 'anypasswd';" || return 1 } echo "Starting bouncer" $BOUNCER_EXE -d $BOUNCER_INI sleep 1 # # fw hacks # fw_drop_port() { case `uname` in Linux) sudo iptables -A OUTPUT -p tcp --dport $1 -j DROP;; Darwin|OpenBSD) echo "block drop out proto tcp from any to 127.0.0.1 port $1" \ | sudo pfctl -a pgbouncer -f -;; *) echo "Unknown OS";; esac } fw_reject_port() { case `uname` in Linux) sudo iptables -A OUTPUT -p tcp --dport $1 -j REJECT --reject-with tcp-reset;; Darwin|OpenBSD) echo "block return-rst out proto tcp from any to 127.0.0.1 port $1" \ | sudo pfctl -a pgbouncer -f -;; *) echo "Unknown OS";; esac } fw_reset() { case `uname` in Linux) sudo iptables -F OUTPUT;; Darwin|OpenBSD) pfctl -a pgbouncer -F all;; *) echo "Unknown OS"; exit 1;; esac } # # util functions # complete() { test -f $BOUNCER_PID && kill `cat $BOUNCER_PID` >/dev/null 2>&1 pgctl -m fast stop rm -f $BOUNCER_PID } die() { echo $@ complete exit 1 } admin() { psql -h /tmp -U pgbouncer pgbouncer -c "$@;" || die "Cannot contact bouncer!" } runtest() { printf "`date` running $1 ... " eval $1 >$LOGDIR/$1.log 2>&1 if [ $? -eq 0 ]; then echo "ok" else echo "FAILED" fi date >> $LOGDIR/$1.log # allow background processing to complete wait # start with fresh config kill -HUP `cat $BOUNCER_PID` } # server_lifetime test_server_lifetime() { admin "set server_lifetime=2" psql -c "select now()" p0 sleep 3 rc=`psql -p $PG_PORT -tAqc "select count(1) from pg_stat_activity where usename='bouncer' and datname='p0'" p0` psql -c "select now()" p0 return $rc } # server_idle_timeout test_server_idle_timeout() { admin "set server_idle_timeout=2" psql -c "select now()" p0 sleep 3 rc=`psql -p $PG_PORT -tAqc "select count(1) from pg_stat_activity where usename='bouncer' and datname='p0'" p0` psql -c "select now()" p0 return $rc } # query_timeout test_query_timeout() { admin "set query_timeout=3" psql -c "select pg_sleep(5)" p0 && return 1 return 0 } # client_idle_timeout test_client_idle_timeout() { admin "set client_idle_timeout=2" psql --set ON_ERROR_STOP=1 p0 <<-PSQL_EOF select now(); \! sleep 3 select now(); PSQL_EOF test $? -eq 0 && return 1 return 0 } # server_login_retry test_server_login_retry() { admin "set query_timeout=10" admin "set server_login_retry=1" (pgctl -m fast stop; sleep 3; pgctl start) & sleep 1 psql -c "select now()" p0 rc=$? wait return $rc } # server_connect_timeout - uses netcat to start dummy server test_server_connect_timeout_establish() { which nc >/dev/null || return 1 echo nc $NC_WAIT_OP -l $NC_PORT nc $NC_WAIT_OP -l $NC_PORT >/dev/null & sleep 2 admin "set query_timeout=3" admin "set server_connect_timeout=2" psql -c "select now()" p2 # client will always see query_timeout, need to grep for connect timeout grep "closing because: connect timeout" $BOUNCER_LOG rc=$? # didnt seem to die otherwise killall nc return $rc } # server_connect_timeout - block with iptables test_server_connect_timeout_reject() { test -z $CAN_SUDO && return 1 admin "set query_timeout=5" admin "set server_connect_timeout=3" fw_drop_port $PG_PORT psql -c "select now()" p0 fw_reset # client will always see query_timeout, need to grep for connect timeout grep "closing because: connect failed" $BOUNCER_LOG } # server_check_delay test_server_check_delay() { test -z $CAN_SUDO && return 1 admin "set server_check_delay=2" admin "set server_login_retry=3" admin "set query_timeout=10" psql p0 -c "select now()" fw_reject_port $PG_PORT sleep 3 psql -tAq p0 -c "select 1" >$LOGDIR/test.tmp & sleep 1 fw_reset echo `date` rules flushed wait echo `date` done waiting test "`cat $LOGDIR/test.tmp`" = "1" } # max_client_conn test_max_client_conn() { admin "set max_client_conn=5" admin "show config" for i in {1..4}; do psql p1 -c "select now() as sleeping from pg_sleep(3);" & done # last conn allowed psql p1 -c "select now() as last_conn" || return 1 # exhaust it psql p1 -c "select now() as sleeping from pg_sleep(3);" & sleep 1 # shouldn't be allowed psql p1 -c "select now() as exhausted" && return 1 # should be ok echo 'waiting for clients to complete ...' wait psql p1 -c "select now() as ok" || return 1 return 0 } # - max pool size test_pool_size() { docount() { for i in {1..10}; do psql $1 -c "select pg_sleep(0.5)" & done wait cnt=`psql -tAqc "select count(1) from pg_stat_activity where usename='bouncer' and datname='$1'" $1` echo $cnt } test `docount p0` -ne 2 && return 1 test `docount p1` -ne 5 && return 1 return 0 } # test online restart while clients running test_online_restart() { # max_client_conn=10 # default_pool_size=5 for i in {1..5}; do echo "`date` attempt $i" for j in {1..5}; do psql -c "select now() as sleeping from pg_sleep(2)" p1 & done pid1=`cat $BOUNCER_PID` echo "old bouncer is $pid1" $BOUNCER_EXE -d -R $BOUNCER_INI sleep 2 pid2=`cat $BOUNCER_PID` echo "new bouncer is $pid2" [ $pid1 = $pid2 ] && return 1 done return 0 } # test pause/resume test_pause_resume() { rm -f $LOGDIR/test.tmp for i in {1..50}; do psql -tAq p0 -c 'select 1 from pg_sleep(0.1)' >>$LOGDIR/test.tmp done & for i in {1..5}; do admin "pause" sleep 1 admin "resume" sleep 1 done wait test `wc -l <$LOGDIR/test.tmp` -eq 50 } # test suspend/resume test_suspend_resume() { rm -f $LOGDIR/test.tmp for i in {1..50}; do psql -tAq p0 -c 'select 1 from pg_sleep(0.1)' >>$LOGDIR/test.tmp done & for i in {1..5}; do psql -h /tmp -p $BOUNCER_PORT pgbouncer -U pgbouncer <<-PSQL_EOF suspend; \! sleep 1 resume; \! sleep 1 PSQL_EOF done wait test `wc -l <$LOGDIR/test.tmp` -eq 50 } # test pause/resume test_enable_disable() { rm -f $LOGDIR/test.tmp psql -tAq p0 -c "select 'enabled 1'" >>$LOGDIR/test.tmp 2>&1 admin "disable p0" psql -tAq p0 -c "select 'disabled 1'" >>$LOGDIR/test.tmp 2>&1 admin "enable p0" psql -tAq p0 -c "select 'enabled 2'" >>$LOGDIR/test.tmp 2>&1 grep -q "enabled 1" $LOGDIR/test.tmp || return 1 grep -q "enabled 2" $LOGDIR/test.tmp || return 1 grep -q "disabled 1" $LOGDIR/test.tmp && return 1 grep -q "does not allow" $LOGDIR/test.tmp || return 1 return 0 } # test pool database restart test_database_restart() { admin "set server_login_retry=1" psql p0 -c "select now() as p0_before_restart" pgctl -m fast restart echo `date` restart 1 psql p0 -c "select now() as p0_after_restart" || return 1 # do with some more clients for i in {1..5}; do psql p0 -c "select pg_sleep($i)" & psql p1 -c "select pg_sleep($i)" & done pgctl -m fast restart echo `date` restart 2 wait psql p0 -c "select now() as p0_after_restart" || return 1 } # test connect string change test_database_change() { admin "set server_lifetime=2" db1=`psql -tAq p1 -c "select current_database()"` cp test.ini test.ini.bak sed '/^p1 =/s/dbname=p1/dbname=p0/g' test.ini >test2.ini mv test2.ini test.ini kill -HUP `cat $BOUNCER_PID` sleep 3 db2=`psql -tAq p1 -c "select current_database()"` echo "db1=$db1 db2=$db2" cp test.ini.bak test.ini rm test.ini.bak admin "show databases" admin "show pools" test "$db1" = "p1" -a "$db2" = "p0" } # test connect string change test_auth_user() { admin "set auth_type='md5'" curuser=`psql -d "dbname=authdb user=someuser password=anypasswd" -tAq -c "select current_user;"` echo "curuser=$curuser" test "$curuser" = "someuser" || return 1 curuser2=`psql -d "dbname=authdb user=nouser password=anypasswd" -tAq -c "select current_user;"` echo "curuser2=$curuser2" test "$curuser2" = "" || return 1 curuser2=`psql -d "dbname=authdb user=someuser password=badpasswd" -tAq -c "select current_user;"` echo "curuser2=$curuser2" test "$curuser2" = "" || return 1 admin "show databases" admin "show pools" return 0 } echo "Testing for sudo access." sudo true && CAN_SUDO=1 testlist=" test_server_login_retry test_auth_user test_client_idle_timeout test_server_lifetime test_server_idle_timeout test_query_timeout test_server_connect_timeout_establish test_server_connect_timeout_reject test_server_check_delay test_max_client_conn test_pool_size test_online_restart test_pause_resume test_suspend_resume test_enable_disable test_database_restart test_database_change " if [ $# -gt 0 ]; then testlist=$@ fi for test in $testlist do runtest $test done complete # vim: sts=0 sw=8 noet nosmarttab: pgbouncer-1.7/test/hba_test.eval0000664000175000017500000000307412572127367013731 00000000000000 # peer md5 db user unix peer dbp user unix password db userp unix trust dbz userz unix # hostssl cert db user 10.1.1.1 tls reject db user 10.1.1.1 reject db user 13.1.1.1 # hostnossl reject db user 11.1.1.1 tls md5 db user 11.1.1.1 reject db user 13.1.1.1 # host password db user 127.0.0.2 tls password db user 127.0.0.3 reject db user 127.0.1.4 # db1 filt reject db1x user 127.0.1.4 md5 db1 user 127.0.1.4 # user1 filt md5 db1z user1 15.0.0.1 reject db1z user2 15.0.0.1 # someusers reject db2 user 16.0.0.1 md5 db2 user1 16.0.0.1 md5 db2 user2 16.0.0.1 md5 db2 user3 16.0.0.1 reject db2 user4 16.0.0.1 # manyusers md5 db2 u1 17.0.0.1 md5 db2 u2 17.0.0.1 md5 db2 u3 17.0.0.1 md5 db2 u4 17.0.0.1 md5 db2 u5 17.0.0.1 md5 db2 u6 17.0.0.1 md5 db2 u7 17.0.0.1 md5 db2 u8 17.0.0.1 md5 db2 u9 17.0.0.1 md5 db2 u10 17.0.0.1 md5 db2 u11 17.0.0.1 # manydbs reject d1 user 18.0.0.2 trust d1 t18user 18.0.0.2 trust d2 t18user 18.0.0.2 trust d3 t18user 18.0.0.2 trust d4 t18user 18.0.0.2 trust d5 t18user 18.0.0.2 trust d6 t18user 18.0.0.2 trust d7 t18user 18.0.0.2 trust d8 t18user 18.0.0.2 trust d9 t18user 18.0.0.2 trust d10 t18user 18.0.0.2 trust d11 t18user 18.0.0.2 # quoting reject db t19user 19.0.0.2 cert all all 19.0.0.2 cert q1"q2 a , b 19.0.0.2 # bitmask cert mdb muser 199.199.199.199 reject mdb muser 199.199.199.198 reject mdb muser 199.199.199.200 cert mdb2 muser 254.1.1.1 # ipv6 md5 mdb muser ff11:2::1 md5 mdb muser ff22:3::1 trust mdb muser ::1 reject mdb muser ::2 pgbouncer-1.7/test/ssl/0000775000175000017500000000000012635051616012135 500000000000000pgbouncer-1.7/test/ssl/test.ini0000664000175000017500000000424612572127367013552 00000000000000;; database name = connect string [databases] p0 = port=6666 host=localhost dbname=p0 user=bouncer pool_size=2 p1 = port=6666 host=localhost dbname=p1 user=bouncer p2 = port=6668 host=localhost dbname=p2 user=bouncer ;; Configuation section [pgbouncer] ;;; ;;; Administrative settings ;;; logfile = tmp/test.log pidfile = tmp/test.pid ;;; ;;; Where to wait for clients ;;; ; ip address or * which means all ip-s listen_addr = 127.0.0.1 listen_port = 6667 unix_socket_dir = /tmp ;;; ;;; Authentication settings ;;; ; any, trust, plain, crypt, md5 ;auth_type = trust auth_file = tmp/userlist.txt ;;; ;;; Pooler personality questions ;;; ; When server connection is released back to pool: ; session - after client disconnects ; transaction - after transaction finishes ; statement - after statement finishes pool_mode = statement ; When taking idle server into use, this query is ran first. ; ; Query for session pooling: ; ABORT; RESET ALL; SET SESSION AUTHORIZATION DEFAULT ; Query for statement/transaction pooling: ; SELECT 1 ; Empty query disables the functionality server_check_query = select 1 ; If server was used more recently that this many seconds ago, ; skip the check query. If 0, the check query is always ran. server_check_delay = 10 ;;; ;;; Connection limits ;;; ; total number of clients that can connect max_client_conn = 10 default_pool_size = 5 ;;; ;;; Timeouts ;;; ; Close server connection if its been connected longer. server_lifetime = 120 ; Close server connection if its not been used in this time. ; Allows to clean unneccessary connections from pool after peak. server_idle_timeout = 60 ; Cancel connection attepmt if server does not answer takes longer. server_connect_timeout = 15 ; If server login failed (server_connect_timeout or auth failure) ; then wait this many second. server_login_retry = 15 ; Dangerous. Server connection is closed if query does not return ; in this time. Should be used to survive network problems, ; _not_ as statement_timeout. (default: 0) query_timeout = 0 ; Dangerous. Client connection is closed if no activity in this time. ; Should be used to survive network problems. (default: 0) client_idle_timeout = 0 pgbouncer-1.7/test/ssl/TestCA1/0000775000175000017500000000000012635051616013341 500000000000000pgbouncer-1.7/test/ssl/TestCA1/config.ini0000600000175000017500000000113512635046467015225 00000000000000[ca] default_ca = test-ca [test-ca] dir = TestCA1 # top dir database = $dir/index.txt # index file. new_certs_dir = $dir/certs # new certs dir certificate = $dir/ca.crt # The CA cert serial = $dir/serial # serial no file private_key = $dir/ca.key # CA private key default_md = sha256 policy = pol-user [pol-user] C = supplied L = supplied #ST = supplied O = supplied OU = supplied CN = supplied emailAddress = supplied [pol-server] C = supplied L = supplied #ST = supplied O = supplied OU = supplied CN = supplied pgbouncer-1.7/test/ssl/TestCA1/certs/0000775000175000017500000000000012635051616014461 500000000000000pgbouncer-1.7/test/ssl/TestCA1/certs/03.pem0000664000175000017500000000316612635051316015331 00000000000000Certificate: Data: Version: 1 (0x0) Serial Number: 3 (0x3) Signature Algorithm: ecdsa-with-SHA256 Issuer: C=QQ, O=Org1, CN=TestCA1 Validity Not Before: Nov 2 18:51:29 2015 GMT Not After : Nov 15 18:51:29 2043 GMT Subject: C=QQ, L=computer, O=Org1, OU=Dev, CN=random Subject Public Key Info: Public Key Algorithm: id-ecPublicKey Public-Key: (256 bit) pub: 04:61:d8:4f:c8:fd:7a:56:a3:3e:f4:e3:fd:b8:0d: fc:72:9e:3b:74:72:18:fb:d1:10:ae:f7:44:8c:cd: 24:55:c3:34:1a:02:3c:d5:26:66:e0:eb:7e:e8:d4: d0:8b:b8:d7:6b:9d:71:7b:c6:a1:57:fa:e6:87:8f: c8:f5:63:dc:28 ASN1 OID: prime256v1 Signature Algorithm: ecdsa-with-SHA256 30:45:02:21:00:91:0b:37:23:2f:2a:c9:c2:60:51:af:ba:33: 1e:c7:50:6f:f4:fb:2a:ff:2d:f5:c6:0d:fd:f4:e6:7a:3a:9e: 77:02:20:6e:02:7b:f5:16:c1:7a:78:68:af:c2:8e:8b:86:b6: ad:84:b1:2e:4e:f6:80:70:5e:c4:13:dc:c4:84:62:61:92 -----BEGIN CERTIFICATE----- MIIBZDCCAQoCAQMwCgYIKoZIzj0EAwIwLjELMAkGA1UEBhMCUVExDTALBgNVBAoT BE9yZzExEDAOBgNVBAMTB1Rlc3RDQTEwHhcNMTUxMTAyMTg1MTI5WhcNNDMxMTE1 MTg1MTI5WjBOMQswCQYDVQQGEwJRUTERMA8GA1UEBxMIY29tcHV0ZXIxDTALBgNV BAoTBE9yZzExDDAKBgNVBAsTA0RldjEPMA0GA1UEAxMGcmFuZG9tMFkwEwYHKoZI zj0CAQYIKoZIzj0DAQcDQgAEYdhPyP16VqM+9OP9uA38cp47dHIY+9EQrvdEjM0k VcM0GgI81SZm4Ot+6NTQi7jXa51xe8ahV/rmh4/I9WPcKDAKBggqhkjOPQQDAgNI ADBFAiEAkQs3Iy8qycJgUa+6Mx7HUG/0+yr/LfXGDf305no6nncCIG4Ce/UWwXp4 aK/CjouGtq2EsS5O9oBwXsQT3MSEYmGS -----END CERTIFICATE----- pgbouncer-1.7/test/ssl/TestCA1/certs/02.pem0000664000175000017500000000316412635051316015326 00000000000000Certificate: Data: Version: 1 (0x0) Serial Number: 2 (0x2) Signature Algorithm: ecdsa-with-SHA256 Issuer: C=QQ, O=Org1, CN=TestCA1 Validity Not Before: Nov 2 18:51:29 2015 GMT Not After : Nov 15 18:51:29 2043 GMT Subject: C=QQ, L=computer, O=Org1, OU=Dev, CN=bouncer Subject Public Key Info: Public Key Algorithm: id-ecPublicKey Public-Key: (256 bit) pub: 04:78:24:60:bd:49:d0:a7:cc:cd:4c:fd:5f:05:57: 85:b2:82:d7:07:e4:09:ef:26:c3:8d:81:45:ed:ab: 48:e2:49:a7:06:d2:ce:5c:7a:87:9c:29:b4:da:be: 02:d4:60:90:bc:6f:61:13:9b:95:d9:9a:5d:ae:a5: bf:42:46:c7:13 ASN1 OID: prime256v1 Signature Algorithm: ecdsa-with-SHA256 30:44:02:20:5f:94:b0:e5:5f:87:1a:fb:19:ff:44:c6:fa:33: d1:b8:40:37:a8:8f:1a:a1:ce:fd:5e:c2:e1:0a:ae:de:97:9d: 02:20:11:3b:5a:be:4a:02:9a:ee:b0:6c:ab:f1:a4:9b:6a:c9: 03:a8:17:40:3a:9b:44:e6:d8:21:42:f0:4f:c0:fe:4b -----BEGIN CERTIFICATE----- MIIBZDCCAQsCAQIwCgYIKoZIzj0EAwIwLjELMAkGA1UEBhMCUVExDTALBgNVBAoT BE9yZzExEDAOBgNVBAMTB1Rlc3RDQTEwHhcNMTUxMTAyMTg1MTI5WhcNNDMxMTE1 MTg1MTI5WjBPMQswCQYDVQQGEwJRUTERMA8GA1UEBxMIY29tcHV0ZXIxDTALBgNV BAoTBE9yZzExDDAKBgNVBAsTA0RldjEQMA4GA1UEAxMHYm91bmNlcjBZMBMGByqG SM49AgEGCCqGSM49AwEHA0IABHgkYL1J0KfMzUz9XwVXhbKC1wfkCe8mw42BRe2r SOJJpwbSzlx6h5wptNq+AtRgkLxvYRObldmaXa6lv0JGxxMwCgYIKoZIzj0EAwID RwAwRAIgX5Sw5V+HGvsZ/0TG+jPRuEA3qI8aoc79XsLhCq7el50CIBE7Wr5KApru sGyr8aSbaskDqBdAOptE5tghQvBPwP5L -----END CERTIFICATE----- pgbouncer-1.7/test/ssl/TestCA1/certs/01.pem0000664000175000017500000000317412635051316015326 00000000000000Certificate: Data: Version: 1 (0x0) Serial Number: 1 (0x1) Signature Algorithm: ecdsa-with-SHA256 Issuer: C=QQ, O=Org1, CN=TestCA1 Validity Not Before: Nov 2 18:51:29 2015 GMT Not After : Nov 15 18:51:29 2043 GMT Subject: C=QQ, L=computer, O=Org1, OU=db, CN=localhost Subject Public Key Info: Public Key Algorithm: id-ecPublicKey Public-Key: (256 bit) pub: 04:43:67:94:56:08:84:77:1e:ca:2f:00:aa:28:37: af:a6:73:b2:42:f3:97:78:6f:c7:fb:c8:3f:67:4c: fe:36:5d:de:fc:4a:75:39:6a:1c:e9:bf:bf:30:ac: 8b:eb:3c:d1:f2:1a:48:f9:97:e9:3d:75:5e:82:41: d5:a3:91:56:91 ASN1 OID: prime256v1 Signature Algorithm: ecdsa-with-SHA256 30:45:02:21:00:bb:bb:d5:e7:e1:2e:30:24:9c:48:48:32:d4: aa:b4:b0:05:41:28:50:c8:4e:53:3d:56:49:06:f6:09:84:93: 98:02:20:5c:41:08:61:05:c7:48:18:bb:f5:6e:c5:7a:74:35: b1:10:cd:cf:75:55:83:43:36:a8:e2:02:7e:44:24:7d:5c -----BEGIN CERTIFICATE----- MIIBZjCCAQwCAQEwCgYIKoZIzj0EAwIwLjELMAkGA1UEBhMCUVExDTALBgNVBAoT BE9yZzExEDAOBgNVBAMTB1Rlc3RDQTEwHhcNMTUxMTAyMTg1MTI5WhcNNDMxMTE1 MTg1MTI5WjBQMQswCQYDVQQGEwJRUTERMA8GA1UEBxMIY29tcHV0ZXIxDTALBgNV BAoTBE9yZzExCzAJBgNVBAsTAmRiMRIwEAYDVQQDEwlsb2NhbGhvc3QwWTATBgcq hkjOPQIBBggqhkjOPQMBBwNCAARDZ5RWCIR3HsovAKooN6+mc7JC85d4b8f7yD9n TP42Xd78SnU5ahzpv78wrIvrPNHyGkj5l+k9dV6CQdWjkVaRMAoGCCqGSM49BAMC A0gAMEUCIQC7u9Xn4S4wJJxISDLUqrSwBUEoUMhOUz1WSQb2CYSTmAIgXEEIYQXH SBi79W7FenQ1sRDNz3VVg0M2qOICfkQkfVw= -----END CERTIFICATE----- pgbouncer-1.7/test/ssl/TestCA1/ca.key0000664000175000017500000000045612635051316014360 00000000000000-----BEGIN EC PARAMETERS----- BggqhkjOPQMBBw== -----END EC PARAMETERS----- -----BEGIN EC PRIVATE KEY----- MHcCAQEEIFRzmArkwfwzfIUXYTj1fz1kFcr9EG+esqwryTUq62HToAoGCCqGSM49 AwEHoUQDQgAEhpnYGAUQkMedJE6ogfOleOvvhsVzSSppxEWLzr0oWqX4Q/PbvsWQ KoXnx48aj0BMi3pvF+nqJuQG1PsXTmLpHw== -----END EC PRIVATE KEY----- pgbouncer-1.7/test/ssl/TestCA1/sites/0000775000175000017500000000000012635051616014470 500000000000000pgbouncer-1.7/test/ssl/TestCA1/sites/01-localhost.key0000664000175000017500000000045612635051316017332 00000000000000-----BEGIN EC PARAMETERS----- BggqhkjOPQMBBw== -----END EC PARAMETERS----- -----BEGIN EC PRIVATE KEY----- MHcCAQEEIKbYtlQ55hDDsDxMJh4jApq+0VlG5dBz3uarAr7rAGC4oAoGCCqGSM49 AwEHoUQDQgAEQ2eUVgiEdx7KLwCqKDevpnOyQvOXeG/H+8g/Z0z+Nl3e/Ep1OWoc 6b+/MKyL6zzR8hpI+ZfpPXVegkHVo5FWkQ== -----END EC PRIVATE KEY----- pgbouncer-1.7/test/ssl/TestCA1/sites/03-random.key0000664000175000017500000000045612635051316016624 00000000000000-----BEGIN EC PARAMETERS----- BggqhkjOPQMBBw== -----END EC PARAMETERS----- -----BEGIN EC PRIVATE KEY----- MHcCAQEEIIEPwOJ8lZNOWm9zmgj38p0WaL839An9RJNFVbQNltcjoAoGCCqGSM49 AwEHoUQDQgAEYdhPyP16VqM+9OP9uA38cp47dHIY+9EQrvdEjM0kVcM0GgI81SZm 4Ot+6NTQi7jXa51xe8ahV/rmh4/I9WPcKA== -----END EC PRIVATE KEY----- pgbouncer-1.7/test/ssl/TestCA1/sites/03-random.csr0000664000175000017500000000066412635051316016624 00000000000000-----BEGIN CERTIFICATE REQUEST----- MIIBCDCBsAIBADBOMQ8wDQYDVQQDEwZyYW5kb20xCzAJBgNVBAYTAlFRMQ0wCwYD VQQKEwRPcmcxMREwDwYDVQQHEwhjb21wdXRlcjEMMAoGA1UECxMDRGV2MFkwEwYH KoZIzj0CAQYIKoZIzj0DAQcDQgAEYdhPyP16VqM+9OP9uA38cp47dHIY+9EQrvdE jM0kVcM0GgI81SZm4Ot+6NTQi7jXa51xe8ahV/rmh4/I9WPcKKAAMAkGByqGSM49 BAEDSAAwRQIhAOz1EZLE+5x0dXV8SkcDYYH9OjlhhvEySBixQy9kHA+VAiBcbfGK l2tVsxuYbu3Qv9KOuda+0t6RKkuPVkP0zDZgNg== -----END CERTIFICATE REQUEST----- pgbouncer-1.7/test/ssl/TestCA1/sites/02-bouncer.key0000664000175000017500000000045612635051316017000 00000000000000-----BEGIN EC PARAMETERS----- BggqhkjOPQMBBw== -----END EC PARAMETERS----- -----BEGIN EC PRIVATE KEY----- MHcCAQEEICB7ePrPDhy/lN9C82KbSZvajGKP2S9w9qcxwxWLlxTloAoGCCqGSM49 AwEHoUQDQgAEeCRgvUnQp8zNTP1fBVeFsoLXB+QJ7ybDjYFF7atI4kmnBtLOXHqH nCm02r4C1GCQvG9hE5uV2ZpdrqW/QkbHEw== -----END EC PRIVATE KEY----- pgbouncer-1.7/test/ssl/TestCA1/sites/02-bouncer.crt0000664000175000017500000000316412635051316016777 00000000000000Certificate: Data: Version: 1 (0x0) Serial Number: 2 (0x2) Signature Algorithm: ecdsa-with-SHA256 Issuer: C=QQ, O=Org1, CN=TestCA1 Validity Not Before: Nov 2 18:51:29 2015 GMT Not After : Nov 15 18:51:29 2043 GMT Subject: C=QQ, L=computer, O=Org1, OU=Dev, CN=bouncer Subject Public Key Info: Public Key Algorithm: id-ecPublicKey Public-Key: (256 bit) pub: 04:78:24:60:bd:49:d0:a7:cc:cd:4c:fd:5f:05:57: 85:b2:82:d7:07:e4:09:ef:26:c3:8d:81:45:ed:ab: 48:e2:49:a7:06:d2:ce:5c:7a:87:9c:29:b4:da:be: 02:d4:60:90:bc:6f:61:13:9b:95:d9:9a:5d:ae:a5: bf:42:46:c7:13 ASN1 OID: prime256v1 Signature Algorithm: ecdsa-with-SHA256 30:44:02:20:5f:94:b0:e5:5f:87:1a:fb:19:ff:44:c6:fa:33: d1:b8:40:37:a8:8f:1a:a1:ce:fd:5e:c2:e1:0a:ae:de:97:9d: 02:20:11:3b:5a:be:4a:02:9a:ee:b0:6c:ab:f1:a4:9b:6a:c9: 03:a8:17:40:3a:9b:44:e6:d8:21:42:f0:4f:c0:fe:4b -----BEGIN CERTIFICATE----- MIIBZDCCAQsCAQIwCgYIKoZIzj0EAwIwLjELMAkGA1UEBhMCUVExDTALBgNVBAoT BE9yZzExEDAOBgNVBAMTB1Rlc3RDQTEwHhcNMTUxMTAyMTg1MTI5WhcNNDMxMTE1 MTg1MTI5WjBPMQswCQYDVQQGEwJRUTERMA8GA1UEBxMIY29tcHV0ZXIxDTALBgNV BAoTBE9yZzExDDAKBgNVBAsTA0RldjEQMA4GA1UEAxMHYm91bmNlcjBZMBMGByqG SM49AgEGCCqGSM49AwEHA0IABHgkYL1J0KfMzUz9XwVXhbKC1wfkCe8mw42BRe2r SOJJpwbSzlx6h5wptNq+AtRgkLxvYRObldmaXa6lv0JGxxMwCgYIKoZIzj0EAwID RwAwRAIgX5Sw5V+HGvsZ/0TG+jPRuEA3qI8aoc79XsLhCq7el50CIBE7Wr5KApru sGyr8aSbaskDqBdAOptE5tghQvBPwP5L -----END CERTIFICATE----- pgbouncer-1.7/test/ssl/TestCA1/sites/01-localhost.crt0000664000175000017500000000317412635051316017332 00000000000000Certificate: Data: Version: 1 (0x0) Serial Number: 1 (0x1) Signature Algorithm: ecdsa-with-SHA256 Issuer: C=QQ, O=Org1, CN=TestCA1 Validity Not Before: Nov 2 18:51:29 2015 GMT Not After : Nov 15 18:51:29 2043 GMT Subject: C=QQ, L=computer, O=Org1, OU=db, CN=localhost Subject Public Key Info: Public Key Algorithm: id-ecPublicKey Public-Key: (256 bit) pub: 04:43:67:94:56:08:84:77:1e:ca:2f:00:aa:28:37: af:a6:73:b2:42:f3:97:78:6f:c7:fb:c8:3f:67:4c: fe:36:5d:de:fc:4a:75:39:6a:1c:e9:bf:bf:30:ac: 8b:eb:3c:d1:f2:1a:48:f9:97:e9:3d:75:5e:82:41: d5:a3:91:56:91 ASN1 OID: prime256v1 Signature Algorithm: ecdsa-with-SHA256 30:45:02:21:00:bb:bb:d5:e7:e1:2e:30:24:9c:48:48:32:d4: aa:b4:b0:05:41:28:50:c8:4e:53:3d:56:49:06:f6:09:84:93: 98:02:20:5c:41:08:61:05:c7:48:18:bb:f5:6e:c5:7a:74:35: b1:10:cd:cf:75:55:83:43:36:a8:e2:02:7e:44:24:7d:5c -----BEGIN CERTIFICATE----- MIIBZjCCAQwCAQEwCgYIKoZIzj0EAwIwLjELMAkGA1UEBhMCUVExDTALBgNVBAoT BE9yZzExEDAOBgNVBAMTB1Rlc3RDQTEwHhcNMTUxMTAyMTg1MTI5WhcNNDMxMTE1 MTg1MTI5WjBQMQswCQYDVQQGEwJRUTERMA8GA1UEBxMIY29tcHV0ZXIxDTALBgNV BAoTBE9yZzExCzAJBgNVBAsTAmRiMRIwEAYDVQQDEwlsb2NhbGhvc3QwWTATBgcq hkjOPQIBBggqhkjOPQMBBwNCAARDZ5RWCIR3HsovAKooN6+mc7JC85d4b8f7yD9n TP42Xd78SnU5ahzpv78wrIvrPNHyGkj5l+k9dV6CQdWjkVaRMAoGCCqGSM49BAMC A0gAMEUCIQC7u9Xn4S4wJJxISDLUqrSwBUEoUMhOUz1WSQb2CYSTmAIgXEEIYQXH SBi79W7FenQ1sRDNz3VVg0M2qOICfkQkfVw= -----END CERTIFICATE----- pgbouncer-1.7/test/ssl/TestCA1/sites/03-random.crt0000664000175000017500000000316612635051316016625 00000000000000Certificate: Data: Version: 1 (0x0) Serial Number: 3 (0x3) Signature Algorithm: ecdsa-with-SHA256 Issuer: C=QQ, O=Org1, CN=TestCA1 Validity Not Before: Nov 2 18:51:29 2015 GMT Not After : Nov 15 18:51:29 2043 GMT Subject: C=QQ, L=computer, O=Org1, OU=Dev, CN=random Subject Public Key Info: Public Key Algorithm: id-ecPublicKey Public-Key: (256 bit) pub: 04:61:d8:4f:c8:fd:7a:56:a3:3e:f4:e3:fd:b8:0d: fc:72:9e:3b:74:72:18:fb:d1:10:ae:f7:44:8c:cd: 24:55:c3:34:1a:02:3c:d5:26:66:e0:eb:7e:e8:d4: d0:8b:b8:d7:6b:9d:71:7b:c6:a1:57:fa:e6:87:8f: c8:f5:63:dc:28 ASN1 OID: prime256v1 Signature Algorithm: ecdsa-with-SHA256 30:45:02:21:00:91:0b:37:23:2f:2a:c9:c2:60:51:af:ba:33: 1e:c7:50:6f:f4:fb:2a:ff:2d:f5:c6:0d:fd:f4:e6:7a:3a:9e: 77:02:20:6e:02:7b:f5:16:c1:7a:78:68:af:c2:8e:8b:86:b6: ad:84:b1:2e:4e:f6:80:70:5e:c4:13:dc:c4:84:62:61:92 -----BEGIN CERTIFICATE----- MIIBZDCCAQoCAQMwCgYIKoZIzj0EAwIwLjELMAkGA1UEBhMCUVExDTALBgNVBAoT BE9yZzExEDAOBgNVBAMTB1Rlc3RDQTEwHhcNMTUxMTAyMTg1MTI5WhcNNDMxMTE1 MTg1MTI5WjBOMQswCQYDVQQGEwJRUTERMA8GA1UEBxMIY29tcHV0ZXIxDTALBgNV BAoTBE9yZzExDDAKBgNVBAsTA0RldjEPMA0GA1UEAxMGcmFuZG9tMFkwEwYHKoZI zj0CAQYIKoZIzj0DAQcDQgAEYdhPyP16VqM+9OP9uA38cp47dHIY+9EQrvdEjM0k VcM0GgI81SZm4Ot+6NTQi7jXa51xe8ahV/rmh4/I9WPcKDAKBggqhkjOPQQDAgNI ADBFAiEAkQs3Iy8qycJgUa+6Mx7HUG/0+yr/LfXGDf305no6nncCIG4Ce/UWwXp4 aK/CjouGtq2EsS5O9oBwXsQT3MSEYmGS -----END CERTIFICATE----- pgbouncer-1.7/test/ssl/TestCA1/sites/02-bouncer.csr0000664000175000017500000000066412635051316017000 00000000000000-----BEGIN CERTIFICATE REQUEST----- MIIBCDCBsQIBADBPMRAwDgYDVQQDEwdib3VuY2VyMQswCQYDVQQGEwJRUTENMAsG A1UEChMET3JnMTERMA8GA1UEBxMIY29tcHV0ZXIxDDAKBgNVBAsTA0RldjBZMBMG ByqGSM49AgEGCCqGSM49AwEHA0IABHgkYL1J0KfMzUz9XwVXhbKC1wfkCe8mw42B Re2rSOJJpwbSzlx6h5wptNq+AtRgkLxvYRObldmaXa6lv0JGxxOgADAJBgcqhkjO PQQBA0cAMEQCIFQrYbvpD5MQ5K2cnpJEvV/6YU9yevAwCLow31jFj2RBAiBuhZ5u 87BL7owf+r/G1KK7V07GhJHI7D+Hb6NNzTvILQ== -----END CERTIFICATE REQUEST----- pgbouncer-1.7/test/ssl/TestCA1/sites/01-localhost.csr0000664000175000017500000000066412635051316017332 00000000000000-----BEGIN CERTIFICATE REQUEST----- MIIBCTCBsgIBADBQMRIwEAYDVQQDEwlsb2NhbGhvc3QxCzAJBgNVBAYTAlFRMQ0w CwYDVQQKEwRPcmcxMREwDwYDVQQHEwhjb21wdXRlcjELMAkGA1UECxMCZGIwWTAT BgcqhkjOPQIBBggqhkjOPQMBBwNCAARDZ5RWCIR3HsovAKooN6+mc7JC85d4b8f7 yD9nTP42Xd78SnU5ahzpv78wrIvrPNHyGkj5l+k9dV6CQdWjkVaRoAAwCQYHKoZI zj0EAQNHADBEAiAhY7+n6M4b3SPkqCFD2EZQVaq3Tb2iNeZXjkJRgFcqQAIgEFQt n/TI3cU+D1K79A3Msc3OTDLiG9csY+oj6hL6jYM= -----END CERTIFICATE REQUEST----- pgbouncer-1.7/test/ssl/TestCA1/serial0000600000175000017500000000000312635046470014444 0000000000000004 pgbouncer-1.7/test/ssl/TestCA1/index.txt0000664000175000017500000000032212635051316015123 00000000000000V 431115185129Z 01 unknown /C=QQ/L=computer/O=Org1/OU=db/CN=localhost V 431115185129Z 02 unknown /C=QQ/L=computer/O=Org1/OU=Dev/CN=bouncer V 431115185129Z 03 unknown /C=QQ/L=computer/O=Org1/OU=Dev/CN=random pgbouncer-1.7/test/ssl/TestCA1/ca.crt0000664000175000017500000000077112635051316014360 00000000000000-----BEGIN CERTIFICATE----- MIIBSDCB8QIJALlVrWqKaqriMAkGByqGSM49BAEwLjELMAkGA1UEBhMCUVExDTAL BgNVBAoTBE9yZzExEDAOBgNVBAMTB1Rlc3RDQTEwHhcNMTUxMTAyMTg1MTI5WhcN NDMxMTE1MTg1MTI5WjAuMQswCQYDVQQGEwJRUTENMAsGA1UEChMET3JnMTEQMA4G A1UEAxMHVGVzdENBMTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABIaZ2BgFEJDH nSROqIHzpXjr74bFc0kqacRFi869KFql+EPz277FkCqF58ePGo9ATIt6bxfp6ibk BtT7F05i6R8wCQYHKoZIzj0EAQNHADBEAiBcrNnGaoJyb9gc2hH5+h1dGwWTKihe EFK0PXQ4Wfs8xAIgfYS7sOe48XrB9RkrE2PEiYYWnHoWvHLuVZDa+gSVoQI= -----END CERTIFICATE----- pgbouncer-1.7/test/ssl/lib.sh0000664000175000017500000000150012572127367013162 00000000000000#! /bin/sh # PEM format # req fields # C = Country # ST = State/Province # L = Locality # O = Organization # OU = Org Unit # CN = commonName # ? = emailAddress umask 077 run() { echo '$' "$@" "$@" 2>&1 | sed 's/^/ > /' } # key -> csr run_req() { tmp="csr.template" args="" while test "$1" != '--'; do args="$args $1" shift done shift ( echo "[req]" echo "prompt=no" echo "distinguished_name=req_distinguished_name" echo "[req_distinguished_name]" for arg; do echo "$arg"; done ) > "$tmp" run openssl req $args -config "$tmp" rm -f csr.template } run_ca() { ser=`cat ${CaName}/serial` run openssl ca -batch -config "${CaName}/config.ini" "$@" while test "$1" != '-out'; do shift done if test "$1" = '-out'; then cp "${CaName}/certs/$ser.pem" "$2" fi } pgbouncer-1.7/test/ssl/newsite.sh0000775000175000017500000000102212572127367014074 00000000000000#! /bin/sh # new server key + cert under some CA test -n "$2" || { echo "usage: $0 " exit 1 } test -f "$1/ca.key" || { echo "CA $1 does not exist" exit 1 } days=10240 . ./lib.sh CaName="$1" DstName="$2" shift 2 ser=`cat $CaName/serial` pfx=$CaName/sites/${ser}-$DstName run openssl ecparam -genkey -name prime256v1 -out $pfx.key # cert reqs run_req -new -key "$pfx.key" -out "$pfx.csr" -- CN="$DstName" "$@" # accept certs run_ca -days $days -policy pol-server -in "$pfx.csr" -out "$pfx.crt" pgbouncer-1.7/test/ssl/newca.sh0000775000175000017500000000220412572127367013516 00000000000000#! /bin/sh # create new CA set -e test -n "$1" || { echo "usage: $0 CaName [K=V]*" exit 1 } test -d "$1" && { echo "CA '$1' already exists" exit 1 } name="$1" shift mkdir -p "$name"/certs mkdir -p "$name"/sites touch "$name"/index.txt echo 01 > "$name"/serial . ./lib.sh days=10240 #run openssl genrsa -out "$name/ca.key" $ksize run openssl ecparam -name prime256v1 -genkey -out "$name/ca.key" # self-signed cert run_req -new -x509 -days $days -key "$name/ca.key" -out "$name/ca.crt" -- "$@" cat > "$name"/config.ini < /dev/null export PATH=/usr/lib/postgresql/9.4/bin:$PATH export PGDATA=$PWD/pgdata export PGHOST=localhost export PGPORT=6667 export EF_ALLOW_MALLOC_0=1 mkdir -p tmp BOUNCER_LOG=tmp/test.log BOUNCER_INI=test.ini BOUNCER_PID=tmp/test.pid BOUNCER_PORT=`sed -n '/^listen_port/s/listen_port.*=[^0-9]*//p' $BOUNCER_INI` BOUNCER_EXE="../../pgbouncer" LOGDIR=tmp NC_PORT=6668 PG_PORT=6666 PG_LOG=$LOGDIR/pg.log pgctl() { pg_ctl -o "-p $PG_PORT" -D $PGDATA $@ >>$PG_LOG 2>&1 } rm -f core ulimit -c unlimited for f in pgdata/postmaster.pid tmp/test.pid; do test -f $f && { kill `cat $f` || true; } done mkdir -p $LOGDIR rm -fr $BOUNCER_LOG $PG_LOG rm -rr $PGDATA if [ ! -d $PGDATA ]; then echo "initdb" mkdir $PGDATA initdb --nosync >> $PG_LOG 2>&1 sed -r -i "/unix_socket_director/s:.*(unix_socket_director.*=).*:\\1 '/tmp':" pgdata/postgresql.conf echo "port = $PG_PORT" >> pgdata/postgresql.conf echo "log_connections = on" >> pgdata/postgresql.conf echo "log_disconnections = on" >> pgdata/postgresql.conf cp pgdata/postgresql.conf pgdata/postgresql.conf.orig cp pgdata/pg_hba.conf pgdata/pg_hba.conf.orig cp pgdata/pg_ident.conf pgdata/pg_ident.conf.orig cp -p TestCA1/sites/01-localhost.crt pgdata/server.crt cp -p TestCA1/sites/01-localhost.key pgdata/server.key cp -p TestCA1/ca.crt pgdata/root.crt echo '"bouncer" "zzz"' > tmp/userlist.txt chmod 600 pgdata/server.key chmod 600 tmp/userlist.txt fi pgctl start sleep 5 echo "createdb" psql -p $PG_PORT -l | grep p0 > /dev/null || { psql -p $PG_PORT -c "create user bouncer" template1 createdb -p $PG_PORT p0 createdb -p $PG_PORT p1 } $BOUNCER_EXE -d $BOUNCER_INI sleep 1 reconf_bouncer() { cp test.ini tmp/test.ini for ln in "$@"; do echo "$ln" >> tmp/test.ini done test -f tmp/test.pid && kill `cat tmp/test.pid` sleep 1 $BOUNCER_EXE -v -v -v -d tmp/test.ini } reconf_pgsql() { cp pgdata/postgresql.conf.orig pgdata/postgresql.conf for ln in "$@"; do echo "$ln" >> pgdata/postgresql.conf done pgctl stop pgctl start sleep 1 } # # fw hacks # # # util functions # complete() { test -f $BOUNCER_PID && kill `cat $BOUNCER_PID` >/dev/null 2>&1 pgctl -m fast stop rm -f $BOUNCER_PID } die() { echo $@ complete exit 1 } admin() { psql -h /tmp -U pgbouncer pgbouncer -c "$@;" || die "Cannot contact bouncer!" } runtest() { echo -n "`date` running $1 ... " eval $1 >$LOGDIR/$1.log 2>&1 if [ $? -eq 0 ]; then echo "ok" else echo "FAILED" fi date >> $LOGDIR/$1.log # allow background processing to complete wait # start with fresh config kill -HUP `cat $BOUNCER_PID` } psql_pg() { psql -U bouncer -h 127.0.0.1 -p $PG_PORT "$@" } psql_bouncer() { PGUSER=bouncer psql "$@" } # server_lifetime test_server_ssl() { reconf_bouncer "auth_type = trust" "server_tls_sslmode = require" echo "hostssl all all 127.0.0.1/32 trust" > pgdata/pg_hba.conf reconf_pgsql "ssl=on" "ssl_ca_file='root.crt'" psql_bouncer -q -d p0 -c "select 'ssl-connect'" | tee tmp/test.tmp0 grep -q "ssl-connect" tmp/test.tmp0 rc=$? return $rc } test_server_ssl_verify() { reconf_bouncer "auth_type = trust" \ "server_tls_sslmode = verify-full" \ "server_tls_ca_file = TestCA1/ca.crt" echo "hostssl all all 127.0.0.1/32 trust" > pgdata/pg_hba.conf reconf_pgsql "ssl=on" "ssl_ca_file='root.crt'" psql_bouncer -q -d p0 -c "select 'ssl-full-connect'" | tee tmp/test.tmp1 grep -q "ssl-full-connect" tmp/test.tmp1 rc=$? return $rc } test_server_ssl_pg_auth() { reconf_bouncer "auth_type = trust" \ "server_tls_sslmode = verify-full" \ "server_tls_ca_file = TestCA1/ca.crt" \ "server_tls_key_file = TestCA1/sites/02-bouncer.key" \ "server_tls_cert_file = TestCA1/sites/02-bouncer.crt" echo "hostssl all all 127.0.0.1/32 cert" > pgdata/pg_hba.conf reconf_pgsql "ssl=on" "ssl_ca_file='root.crt'" psql_bouncer -q -d p0 -c "select 'ssl-cert-connect'" | tee tmp/test.tmp2 grep "ssl-cert-connect" tmp/test.tmp2 rc=$? return $rc } test_client_ssl() { reconf_bouncer "auth_type = trust" "server_tls_sslmode = prefer" \ "client_tls_sslmode = require" \ "client_tls_key_file = TestCA1/sites/01-localhost.key" \ "client_tls_cert_file = TestCA1/sites/01-localhost.crt" echo "host all all 127.0.0.1/32 trust" > pgdata/pg_hba.conf reconf_pgsql "ssl=on" "ssl_ca_file='root.crt'" psql_bouncer -q -d "dbname=p0 sslmode=require" -c "select 'client-ssl-connect'" | tee tmp/test.tmp grep -q "client-ssl-connect" tmp/test.tmp rc=$? return $rc } test_client_ssl() { reconf_bouncer "auth_type = trust" "server_tls_sslmode = prefer" \ "client_tls_sslmode = require" \ "client_tls_key_file = TestCA1/sites/01-localhost.key" \ "client_tls_cert_file = TestCA1/sites/01-localhost.crt" echo "host all all 127.0.0.1/32 trust" > pgdata/pg_hba.conf reconf_pgsql "ssl=on" "ssl_ca_file='root.crt'" psql_bouncer -q -d "dbname=p0 sslmode=verify-full sslrootcert=TestCA1/ca.crt" -c "select 'client-ssl-connect'" | tee tmp/test.tmp 2>&1 grep -q "client-ssl-connect" tmp/test.tmp rc=$? return $rc } test_client_ssl_auth() { reconf_bouncer "auth_type = cert" "server_tls_sslmode = prefer" \ "client_tls_sslmode = verify-full" \ "client_tls_ca_file = TestCA1/ca.crt" \ "client_tls_key_file = TestCA1/sites/01-localhost.key" \ "client_tls_cert_file = TestCA1/sites/01-localhost.crt" echo "host all all 127.0.0.1/32 trust" > pgdata/pg_hba.conf reconf_pgsql "ssl=on" "ssl_ca_file='root.crt'" psql_bouncer -q -d "dbname=p0 sslmode=require sslkey=TestCA1/sites/02-bouncer.key sslcert=TestCA1/sites/02-bouncer.crt" \ -c "select 'client-ssl-connect'" | tee tmp/test.tmp 2>&1 grep -q "client-ssl-connect" tmp/test.tmp rc=$? return $rc } testlist=" test_server_ssl test_server_ssl_verify test_server_ssl_pg_auth test_client_ssl test_client_ssl_auth " if [ $# -gt 0 ]; then testlist="$*" fi for test in $testlist do runtest $test done complete # vim: sts=0 sw=8 noet nosmarttab: pgbouncer-1.7/test/ssl/Makefile0000664000175000017500000000113312616107364013514 00000000000000 EXTRA_DIST = lib.sh newca.sh newsite.sh test.ini test.sh Makefile \ TestCA1/ca.crt TestCA1/ca.key TestCA1/config.ini \ TestCA1/index.txt TestCA1/serial \ TestCA1/certs/01.pem TestCA1/certs/02.pem TestCA1/certs/03.pem \ TestCA1/sites/01-localhost.crt TestCA1/sites/01-localhost.key TestCA1/sites/01-localhost.csr \ TestCA1/sites/02-bouncer.crt TestCA1/sites/02-bouncer.key TestCA1/sites/02-bouncer.csr \ TestCA1/sites/03-random.crt TestCA1/sites/03-random.key TestCA1/sites/03-random.csr SUBLOC = test/ssl include ../../config.mak include ../../lib/mk/antimake.mk pgbouncer-1.7/test/asynctest.c0000664000175000017500000003105512556777177013464 00000000000000/* * Things to test: * - Conn per query * - show tx * - long tx * - variable-size query */ #include "system.h" #ifdef WIN32 #undef strerror #undef main #endif #include #include #include static void log_error(const char *, ...); static void log_debug(const char *, ...); static void fatal(const char *fmt, ...); static void fatal_noexit(const char *fmt, ...); #include "list.h" static char *simple_query = "select 1"; typedef void (*libev_cb_f)(int sock, short flags, void *arg); typedef struct DbConn { List head; const char *connstr; struct event ev; PGconn *con; bool logged_in; /* time_t connect_time; */ int query_count; /* const char *query; */ int _arglen; } DbConn; #define QT_SIMPLE 1 #define QT_BIGDATA 2 #define QT_SLEEP 4 static unsigned QueryTypes = 0; static uint64_t LoginOkCount = 0; static uint64_t LoginFailedCount = 0; static uint64_t SqlErrorCount = 0; static uint64_t QueryCount = 0; static char *bulk_data; static int bulk_data_max = 128*1024; /* power of 2 */ static int verbose = 0; static int throttle_connects = 0; static int throttle_queries = 0; static int per_conn_queries = 1; static STATLIST(idle_list); static STATLIST(active_list); static usec_t _time_cache = 0; /* * utility functions */ static usec_t get_time_usec(void) { struct timeval tv; gettimeofday(&tv, NULL); return (usec_t)tv.tv_sec * USEC + tv.tv_usec; } static usec_t get_cached_time(void) { if (!_time_cache) _time_cache = get_time_usec(); return _time_cache; } static void reset_time_cache(void) { _time_cache = 0; } /* fill mem with random junk */ static void init_bulk_data(void) { int i; bulk_data = malloc(bulk_data_max + 1); for (i = 0; i < bulk_data_max; i++) { bulk_data[i] = 'a' + (i % 26); } bulk_data[i] = 0; } static DbConn *new_db(const char *connstr) { DbConn *db = malloc(sizeof(*db)); memset(db, 0, sizeof(*db)); list_init(&db->head); db->connstr = connstr; return db; } static void set_idle(DbConn *db) { Assert(item_in_list(&db->head, &active_list.head)); statlist_remove(&db->head, &active_list); statlist_append(&db->head, &idle_list); log_debug("%p: set_idle", db); } static void set_active(DbConn *db) { Assert(item_in_list(&db->head, &idle_list.head)); statlist_remove(&db->head, &idle_list); statlist_append(&db->head, &active_list); log_debug("%p: set_active", db); } static void fatal_perror(const char *err) { log_error("%s: %s", err, strerror(errno)); exit(1); } static void fatal_noexit(const char *fmt, ...) { va_list ap; char buf[1024]; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); printf("FATAL: %s\n", buf); } static void fatal(const char *fmt, ...) { va_list ap; char buf[1024]; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); printf("FATAL: %s\n", buf); exit(1); } static void log_debug(const char *fmt, ...) { va_list ap; char buf[1024]; if (verbose == 0) return; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); printf("dbg: %s\n", buf); } static void log_error(const char *fmt, ...) { va_list ap; char buf[1024]; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); printf("ERR: %s\n", buf); } static void wait_event(DbConn *db, short ev, libev_cb_f fn) { event_set(&db->ev, PQsocket(db->con), ev, fn, db); if (event_add(&db->ev, NULL) < 0) fatal_perror("event_add"); } static void disconnect(DbConn *db, bool is_err, const char *reason, ...) { char buf[1024]; va_list ap; if (is_err) { if (db->logged_in) SqlErrorCount++; else LoginFailedCount++; } if (db->con) { va_start(ap, reason); vsnprintf(buf, sizeof(buf), reason, ap); va_end(ap); log_debug("disconnect because: %s", buf); PQfinish(db->con); db->con = NULL; db->logged_in = 0; set_idle(db); } } /* some error happened */ static void conn_error(DbConn *db, const char *desc) { static int ecount = 0; if (db->con) { if (ecount++ < 3) printf("\r%s (arglen=%d)\n", PQerrorMessage(db->con), db->_arglen); disconnect(db, true, "%s: %s", desc, PQerrorMessage(db->con)); } else { printf("random error: %s\n", desc); exit(1); } } /* * Connection has a resultset available, fetch it. * * Returns true if there may be more results coming, * false if all done. */ static bool another_result(DbConn *db) { PGresult *res; /* got one */ res = PQgetResult(db->con); if (res == NULL) { QueryCount++; set_idle(db); return false; } switch (PQresultStatus(res)) { case PGRES_TUPLES_OK: /* TODO: check result */ if (db->_arglen > 0) { int curlen = strlen(PQgetvalue(res, 0, 0)); if (curlen != db->_arglen) { printf("result does not match: sent=%d got=%d\n", db->_arglen, curlen); } } case PGRES_COMMAND_OK: PQclear(res); break; default: PQclear(res); conn_error(db, "weird result"); return false; } return true; } /* * Called when select() told that conn is avail for reading/writing. * * It should call postgres handlers and then change state if needed. */ static void result_cb(int sock, short flags, void *arg) { DbConn *db = arg; int res; res = PQconsumeInput(db->con); if (res == 0) { conn_error(db, "PQconsumeInput"); return; } /* loop until PQgetResult returns NULL */ while (1) { /* if PQisBusy, then incomplete result */ if (PQisBusy(db->con)) { wait_event(db, EV_READ, result_cb); break; } /* got one */ if (!another_result(db)) break; } } static void send_cb(int sock, short flags, void *arg) { int res; DbConn *db = arg; res = PQflush(db->con); if (res > 0) { wait_event(db, EV_WRITE, send_cb); } else if (res == 0) { wait_event(db, EV_READ, result_cb); } else conn_error(db, "PQflush"); } static int send_query_bigdata(DbConn *db) { const char *values[1]; int lengths[1]; int fmts[1]; int arglen; char *q = "select $1::text"; arglen = random() % bulk_data_max; db->_arglen = arglen; values[0] = bulk_data + bulk_data_max - arglen; lengths[0] = arglen; fmts[0] = 1; return PQsendQueryParams(db->con, q, 1, NULL, values, lengths, fmts, 1); } static int send_query_sleep(DbConn *db) { const char *q = "select pg_sleep(0.2)"; return PQsendQueryParams(db->con, q, 0, NULL, NULL, NULL, NULL, 0); } static int send_query_simple(DbConn *db) { const char *q = simple_query; return PQsendQueryParams(db->con, q, 0, NULL, NULL, NULL, NULL, 0); } /* send the query to server connection */ static void send_query(DbConn *db) { int res; if (db->query_count >= per_conn_queries) { disconnect(db, false, "query count full"); return; } db->query_count++; /* send query */ if (QueryTypes & QT_SLEEP) { res = send_query_sleep(db); } else if (QueryTypes & QT_BIGDATA) { res = send_query_bigdata(db); } else { res = send_query_simple(db); } if (!res) { conn_error(db, "PQsendQueryParams"); return; } /* flush it down */ res = PQflush(db->con); if (res > 0) { wait_event(db, EV_WRITE, send_cb); } else if (res == 0) { wait_event(db, EV_READ, result_cb); } else conn_error(db, "PQflush"); } static void connect_cb(int sock, short flags, void *arg) { DbConn *db = arg; PostgresPollingStatusType poll_res; poll_res = PQconnectPoll(db->con); switch (poll_res) { case PGRES_POLLING_WRITING: wait_event(db, EV_WRITE, connect_cb); break; case PGRES_POLLING_READING: wait_event(db, EV_READ, connect_cb); break; case PGRES_POLLING_OK: log_debug("login ok: fd=%d", PQsocket(db->con)); LoginOkCount++; db->logged_in = 1; send_query(db); break; default: conn_error(db, "PQconnectPoll"); } } static void launch_connect(DbConn *db) { /* launch new connection */ db->logged_in = 0; db->query_count = 0; db->con = PQconnectStart(db->connstr); if (db->con == NULL) { log_error("PQconnectStart: no mem"); exit(1); } if (PQstatus(db->con) == CONNECTION_BAD) { conn_error(db, "PQconnectStart"); return; } wait_event(db, EV_WRITE, connect_cb); } #define ACT_ONCE 10 static void handle_idle(void) { DbConn *db; List *item, *tmp; int allow_connects = 100000; int allow_queries = 100000; static usec_t startup_time = 0; usec_t now = get_cached_time(); usec_t diff; int once; if (startup_time == 0) startup_time = get_cached_time(); diff = now - startup_time; if (diff < USEC) diff = USEC; if (throttle_connects > 0) { allow_connects = throttle_connects - LoginOkCount * USEC / diff; once = throttle_connects / ACT_ONCE; if (!once) once = 1; if (once < allow_connects) allow_connects = once; } if (throttle_queries > 0) { allow_queries = throttle_queries - QueryCount * USEC / diff; once = throttle_connects / ACT_ONCE; if (!once) once = 1; if (once < allow_connects) allow_connects = once; } statlist_for_each_safe(item, &idle_list, tmp) { db = container_of(item, DbConn, head); if (db->con && allow_queries > 0) { set_active(db); send_query(db); allow_queries--; } else if (allow_connects > 0) { set_active(db); launch_connect(db); allow_connects--; } } } static void run_stats(int fd, short ev, void *arg) { static struct event ev_stats; struct timeval period = { 5, 0 }; static usec_t last_time = 0; static uint64_t last_query_count = 0; static uint64_t last_login_failed_count = 0; static uint64_t last_login_ok_count = 0; static uint64_t last_sql_error_count = 0; double time_diff, qcount_diff, loginerr_diff, loginok_diff, sqlerr_diff; usec_t now = get_cached_time(); time_diff = now - last_time; if (last_time && time_diff) { qcount_diff = QueryCount - last_query_count; loginerr_diff = LoginFailedCount - last_login_failed_count; sqlerr_diff = SqlErrorCount - last_sql_error_count; loginok_diff = LoginOkCount - last_login_ok_count; if (verbose == 0) { printf(">> loginok,loginerr,sqlerr,qcount: %6.1f / %6.1f / %6.1f / %6.1f active/idle: %3d / %3d \r", USEC * loginok_diff / time_diff, USEC * loginerr_diff / time_diff, USEC * sqlerr_diff / time_diff, USEC * qcount_diff / time_diff, statlist_count(&active_list), statlist_count(&idle_list)); fflush(stdout); } } if (!last_time) evtimer_set(&ev_stats, run_stats, NULL); if (evtimer_add(&ev_stats, &period) < 0) fatal_perror("evtimer_add"); last_query_count = QueryCount; last_login_failed_count = LoginFailedCount; last_sql_error_count = SqlErrorCount; last_login_ok_count = LoginOkCount; last_time = now; } static const char usage_str [] = "usage: asynctest [-d connstr][-n numconn][-s seed][-t ][-C maxconn][-Q maxquery][-q perconnq]\n" " -d connstr libpq connect string\n" " -n num number of connections\n" " -s seed random number seed\n" " -t type of queries query type, see below\n" " -C maxcps max number of connects per sec\n" " -Q maxqps max number of queries per sec\n" " -q num queries per connection (default 1)\n" " -S sql set simple query\n" "accepted query types:\n" " B - bigdata\n" " S - sleep occasionally\n" " 1 - simple 'select 1'\n"; int main(int argc, char *argv[]) { int i, c; DbConn *db; unsigned seed = time(NULL) ^ getpid(); char *cstr = NULL; int numcon = 50; #ifdef WIN32 int wsresult; WSADATA wsaData; #endif while ((c = getopt(argc, argv, "S:d:n:s:t:hvC:Q:q:")) != EOF) { switch (c) { default: case 'h': printf("%s", usage_str); return 0; case 'S': simple_query = optarg; break; case 'd': cstr = optarg; break; case 'C': throttle_connects = atoi(optarg); break; case 'Q': throttle_queries = atoi(optarg); break; case 'n': numcon = atoi(optarg); break; case 's': seed = atoi(optarg); break; case 'v': verbose++; break; case 'q': per_conn_queries = atoi(optarg); break; case 't': for (i = 0; optarg[i]; i++) { switch (optarg[i]) { case 'B': QueryTypes = QT_BIGDATA; break; case 'S': QueryTypes = QT_SLEEP; break; case '1': QueryTypes = QT_SIMPLE; break; default: log_error("bad type"); break; } } } } if (!cstr) { printf(usage_str); return 1; } #ifdef WIN32 wsresult = WSAStartup(MAKEWORD(2,0),&wsaData); if (wsresult != 0) { fatal("Cannot start the network subsystem -%d", wsresult); } #endif if (throttle_connects < 0 || throttle_queries < 0 || numcon < 0) fatal("invalid parameter"); if (QueryTypes == 0) QueryTypes = QT_SIMPLE; printf("using seed: %u\n", seed); srandom(seed); init_bulk_data(); for (i = 0; i < numcon; i++) { db = new_db(cstr); statlist_append(&db->head, &idle_list); } event_init(); run_stats(0, 0, NULL); printf("running..\n"); while (1) { handle_idle(); reset_time_cache(); if (event_loop(EVLOOP_ONCE) < 0) log_error("event_loop: %s", strerror(errno)); } return 0; } pgbouncer-1.7/test/Makefile0000664000175000017500000000143712616107364012722 00000000000000 #PGINC = -I$(shell pg_config --includedir) #PGLIB = -L$(shell pg_config --libdir) #CPPFLAGS += -I../include -I../lib $(PGINC) #LDFLAGS += $(PGLIB) #LIBS := -lpq $(LIBS) #ifeq ($(PORTNAME),win32) #CPPFLAGS += -I../win32 #endif USUAL_DIR = ../lib SUBLOC = test DIST_SUBDIRS = ssl EXTRA_DIST = conntest.sh ctest6000.ini ctest7000.ini run-conntest.sh \ hba_test.eval hba_test.rules Makefile \ test.ini test.sh stress.py userlist.txt noinst_PROGRAMS = hba_test hba_test_CPPFLAGS = -I../include hba_test_CFLAGS = -O0 hba_test_SOURCES = hba_test.c ../src/hba.c ../src/util.c hba_test_EMBED_LIBUSUAL = 1 EXTRA_PROGRAMS = asynctest asynctest_SOURCES = asynctest.c AM_FEATURES = libusual include ../config.mak include ../lib/mk/antimake.mk all: run_test run_test: hba_test ./hba_test pgbouncer-1.7/autogen.sh0000775000175000017500000000005412556660473012306 00000000000000#! /bin/sh ./lib/mk/std-autogen.sh ./lib pgbouncer-1.7/doc/0000775000175000017500000000000012635051616011122 500000000000000pgbouncer-1.7/doc/usage.rst0000664000175000017500000003111112572127367012704 00000000000000 pgbouncer ######### Synopsis ======== :: pgbouncer [-d][-R][-v][-u user] pgbouncer -V|-h On Windows computers, the options are:: pgbouncer.exe [-v][-u user] pgbouncer.exe -V|-h Additional options for setting up a Windows service:: pgbouncer.exe --regservice pgbouncer.exe --unregservice DESCRIPTION =========== **pgbouncer** is a PostgreSQL connection pooler. Any target application can be connected to **pgbouncer** as if it were a PostgreSQL server, and **pgbouncer** will create a connection to the actual server, or it will reuse one of its existing connections. The aim of **pgbouncer** is to lower the performance impact of opening new connections to PostgreSQL. In order not to compromise transaction semantics for connection pooling, **pgbouncer** supports several types of pooling when rotating connections: Session pooling Most polite method. When client connects, a server connection will be assigned to it for the whole duration the client stays connected. When the client disconnects, the server connection will be put back into the pool. This is the default method. Transaction pooling A server connection is assigned to client only during a transaction. When PgBouncer notices that transaction is over, the server connection will be put back into the pool. Statement pooling Most aggressive method. The server connection will be put back into pool immediately after a query completes. Multi-statement transactions are disallowed in this mode as they would break. The administration interface of **pgbouncer** consists of some new ``SHOW`` commands available when connected to a special 'virtual' database **pgbouncer**. Quick-start =========== Basic setup and usage as following. 1. Create a pgbouncer.ini file. Details in **pgbouncer(5)**. Simple example:: [databases] template1 = host=127.0.0.1 port=5432 dbname=template1 [pgbouncer] listen_port = 6543 listen_addr = 127.0.0.1 auth_type = md5 auth_file = users.txt logfile = pgbouncer.log pidfile = pgbouncer.pid admin_users = someuser 2. Create ``users.txt`` file that contains users allowed in:: "someuser" "same_password_as_in_server" 3. Launch **pgbouncer**:: $ pgbouncer -d pgbouncer.ini 4. Have your application (or the **psql** client) connect to **pgbouncer** instead of directly to PostgreSQL server:: $ psql -p 6543 -U someuser template1 5. Manage **pgbouncer** by connecting to the special administration database **pgbouncer** and issuing ``show help;`` to begin:: $ psql -p 6543 -U someuser pgbouncer pgbouncer=# show help; NOTICE: Console usage DETAIL: SHOW [HELP|CONFIG|DATABASES|FDS|POOLS|CLIENTS|SERVERS|SOCKETS|LISTS|VERSION] SET key = arg RELOAD PAUSE SUSPEND RESUME SHUTDOWN 6. If you made changes to the pgbouncer.ini file, you can reload it with:: pgbouncer=# RELOAD; Command line switches ===================== -d Run in background. Without it the process will run in foreground. Note: Does not work on Windows, **pgbouncer** need to run as service there. -R Do an online restart. That means connecting to the running process, loading the open sockets from it, and then using them. If there is no active process, boot normally. Note: Works only if OS supports Unix sockets and the `unix_socket_dir` is not disabled in config. Does not work on Windows machines. Does not work with TLS connections, they are dropped. -u user Switch to the given user on startup. -v Increase verbosity. Can be used multiple times. -q Be quiet - do not log to stdout. Note this does not affect logging verbosity, only that stdout is not to be used. For use in init.d scripts. -V Show version. -h Show short help. --regservice Win32: Register pgbouncer to run as Windows service. The **service_name** config parameter value is used as name to register under. --unregservice Win32: Unregister Windows service. Admin console ============= The console is available by connecting as normal to the database **pgbouncer**:: $ psql -p 6543 pgbouncer Only users listed in configuration parameters **admin_users** or **stats_users** are allowed to login to the console. (Except when `auth_mode=any`, then any user is allowed in as a stats_user.) Additionally, the username **pgbouncer** is allowed to log in without password, if the login comes via Unix socket and the client has same Unix user uid as the running process. Show commands ~~~~~~~~~~~~~ The **SHOW** commands output information. Each command is described below. SHOW STATS; ----------- Shows statistics. database Statistics are presented per database. total_requests Total number of SQL requests pooled by **pgbouncer**. total_received Total volume in bytes of network traffic received by **pgbouncer**. total_sent Total volume in bytes of network traffic sent by **pgbouncer**. total_query_time Total number of microseconds spent by **pgbouncer** when actively connected to PostgreSQL. avg_req Average requests per second in last stat period. avg_recv Average received (from clients) bytes per second. avg_sent Average sent (to clients) bytes per second. avg_query Average query duration in microseconds. SHOW SERVERS; ------------- type S, for server. user Username **pgbouncer** uses to connect to server. database Database name. state State of the pgbouncer server connection, one of **active**, **used** or **idle**. addr IP address of PostgreSQL server. port Port of PostgreSQL server. local_addr Connection start address on local machine. local_port Connection start port on local machine. connect_time When the connection was made. request_time When last request was issued. ptr Address of internal object for this connection. Used as unique ID. link Address of client connection the server is paired with. remote_pid Pid of backend server process. In case connection is made over unix socket and OS supports getting process ID info, it's OS pid. Otherwise it's extracted from cancel packet server sent, which should be PID in case server is Postgres, but it's a random number in case server it another PgBouncer. SHOW CLIENTS; ------------- type C, for client. user Client connected user. database Database name. state State of the client connection, one of **active**, **used**, **waiting** or **idle**. addr IP address of client. port Port client is connected to. local_addr Connection end address on local machine. local_port Connection end port on local machine. connect_time Timestamp of connect time. request_time Timestamp of latest client request. ptr Address of internal object for this connection. Used as unique ID. link Address of server connection the client is paired with. remote_pid Process ID, in case client connects over UNIX socket and OS supports getting it. SHOW POOLS; ----------- A new pool entry is made for each couple of (database, user). database Database name. user User name. cl_active Client connections that are linked to server connection and can process queries. cl_waiting Client connections have sent queries but have not yet got a server connection. sv_active Server connections that linked to client. sv_idle Server connections that unused and immediately usable for client queries. sv_used Server connections that have been idle more than `server_check_delay`, so they needs `server_check_query` to run on it before it can be used. sv_tested Server connections that are currently running either `server_reset_query` or `server_check_query`. sv_login Server connections currently in logging in process. maxwait How long the first (oldest) client in queue has waited, in seconds. If this starts increasing, then the current pool of servers does not handle requests quick enough. Reason may be either overloaded server or just too small of a **pool_size** setting. pool_mode The pooling mode in use. SHOW LISTS; ----------- Show following internal information, in columns (not rows): databases Count of databases. users Count of users. pools Count of pools. free_clients Count of free clients. used_clients Count of used clients. login_clients Count of clients in **login** state. free_servers Count of free servers. used_servers Count of used servers. SHOW USERS; ----------- name The user name pool_mode The user's override pool_mode, or NULL if the default will be used instead. SHOW DATABASES; --------------- name Name of configured database entry. host Host pgbouncer connects to. port Port pgbouncer connects to. database Actual database name pgbouncer connects to. force_user When user is part of the connection string, the connection between pgbouncer and PostgreSQL is forced to the given user, whatever the client user. pool_size Maximum number of server connections. pool_mode The database's override pool_mode, or NULL if the default will be used instead. SHOW FDS; --------- Internal command - shows list of fds in use with internal state attached to them. When the connected user has username "pgbouncer", connects through Unix socket and has same UID as running process, the actual fds are passed over the connection. This mechanism is used to do an online restart. Note: This does not work on Windows machines. This command also blocks internal event loop, so it should not be used while PgBouncer is in use. fd File descriptor numeric value. task One of **pooler**, **client** or **server**. user User of the connection using the FD. database Database of the connection using the FD. addr IP address of the connection using the FD, **unix** if a unix socket is used. port Port used by the connection using the FD. cancel Cancel key for this connection. link fd for corresponding server/client. NULL if idle. SHOW CONFIG; ------------ Show the current configuration settings, one per row, with following columns: key Configuration variable name value Configuration value changeable Either **yes** or **no**, shows if the variable can be changed while running. If **no**, the variable can be changed only boot-time. SHOW DNS_HOSTS; --------------- Show hostnames in DNS cache. hostname Host name. ttl How meny seconds until next lookup. addrs Comma separated list of addresses. SHOW DNS_ZONES -------------- Show DNS zones in cache. zonename Zone name. serial Current serial. count Hostnames belonging to this zone. Process controlling commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PAUSE [db]; ----------- PgBouncer tries to disconnect from all servers, first waiting for all queries to complete. The command will not return before all queries are finished. To be used at the time of database restart. If database name is given, only that database will be paused. DISABLE db; ----------- Reject all new client connections on the given database. ENABLE db; ---------- Allow new client connections after a previous **DISABLE** command. KILL db; -------- Immediately drop all client and server connections on given database. SUSPEND; -------- All socket buffers are flushed and PgBouncer stops listening for data on them. The command will not return before all buffers are empty. To be used at the time of PgBouncer online reboot. RESUME [db]; ------------ Resume work from previous **PAUSE** or **SUSPEND** command. SHUTDOWN; --------- The PgBouncer process will exit. RELOAD; ------- The PgBouncer process will reload its configuration file and update changeable settings. Signals ~~~~~~~ SIGHUP Reload config. Same as issuing command **RELOAD;** on console. SIGINT Safe shutdown. Same as issuing **PAUSE;** and **SHUTDOWN;** on console. SIGTERM Immediate shutdown. Same as issuing **SHUTDOWN;** on console. Libevent settings ~~~~~~~~~~~~~~~~~ From libevent docs:: It is possible to disable support for epoll, kqueue, devpoll, poll or select by setting the environment variable EVENT_NOEPOLL, EVENT_NOKQUEUE, EVENT_NODEVPOLL, EVENT_NOPOLL or EVENT_NOSELECT, respectively. By setting the environment variable EVENT_SHOW_METHOD, libevent displays the kernel notification method that it uses. See also ======== pgbouncer(5) - manpage of configuration settings descriptions. https://pgbouncer.github.io/ https://wiki.postgresql.org/wiki/PgBouncer pgbouncer-1.7/doc/frag-usage-man0000664000175000017500000000032712572127367013570 00000000000000-------------------------------------------- lightweight connection pooler for PostgreSQL -------------------------------------------- :Date: 2006-10-23 :Version: 1.7 :Manual section: 5 :Manual group: Databases pgbouncer-1.7/doc/pgbouncer.50000664000175000017500000006245712630310724013123 00000000000000.\" Man page generated from reStructeredText. . .TH PGBOUNCER.INI 5 "2006-10-23" "1.7" "Databases" .SH NAME pgbouncer.ini \- configuration file for pgbouncer . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .SH DESCRIPTION .sp Config file is in "ini" format. Section names are between "[" and "]". Lines starting with ";" or "#" are taken as comments and ignored. The characters ";" and "#" are not recognized when they appear later in the line. .SH GENERIC SETTINGS .SS logfile .sp Specifies log file. Log file is kept open so after rotation \fBkill \-HUP\fP or on console \fBRELOAD;\fP should be done. Note: On Windows machines, the service must be stopped and started. .sp Default: not set. .SS pidfile .sp Specifies the pid file. Without a pidfile, daemonization is not allowed. .sp Default: not set. .SS listen_addr .sp Specifies list of addresses, where to listen for TCP connections. You may also use \fB*\fP meaning "listen on all addresses". When not set, only Unix socket connections are allowed. .sp Addresses can be specified numerically (IPv4/IPv6) or by name. .sp Default: not set .SS listen_port .sp Which port to listen on. Applies to both TCP and Unix sockets. .sp Default: 6432 .SS unix_socket_dir .sp Specifies location for Unix sockets. Applies to both listening socket and server connections. If set to an empty string, Unix sockets are disabled. Required for online reboot (\-R) to work. Note: Not supported on Windows machines. .sp Default: /tmp .SS unix_socket_mode .sp Filesystem mode for unix socket. .sp Default: 0777 .SS unix_socket_group .sp Group name to use for unix socket. .sp Default: not set .SS user .sp If set, specifies the Unix user to change to after startup. Works only if PgBouncer is started as root or if it\(aqs already running as given user. .sp Note: Not supported on Windows machines. .sp Default: not set .SS auth_file .sp The name of the file to load user names and passwords from. The file format is the same as the PostgreSQL 8.x pg_auth/pg_pwd file, so this setting can be pointed directly to one of those backend files. Since version 9.0, PostgreSQL does not use such text file, so it must be generated manually. See section \fI\%Authentication file format\fP below about details. .sp Default: not set. .SS auth_hba_file .sp HBA configuration file to use when \fI\%auth_type\fP is \fBhba\fP. Supported from version 1.7 onwards. .sp Default: not set .SS auth_type .sp How to authenticate users. .INDENT 0.0 .TP .B hba Actual auth type is loaded from \fI\%auth_hba_file\fP. This allows different authentication methods different access paths. Example: connection over unix socket use \fBpeer\fP auth method, connection over TCP must use TLS. Supported from version 1.7 onwards. .TP .B cert Client must connect over TLS connection with valid client cert. Username is then taken from CommonName field from certificate. .TP .B md5 Use MD5\-based password check. \fI\%auth_file\fP may contain both MD5\-encrypted or plain\-text passwords. This is the default authentication method. .TP .B plain Clear\-text password is sent over wire. Deprecated. .TP .B trust No authentication is done. Username must still exist in \fI\%auth_file\fP. .TP .B any Like the \fBtrust\fP method, but the username given is ignored. Requires that all databases are configured to log in as specific user. Additionally, the console database allows any user to log in as admin. .UNINDENT .SS auth_query .sp Query to load user\(aqs password from db. .sp Default: \fBSELECT usename, passwd FROM pg_shadow WHERE usename=$1\fP .SS pool_mode .sp Specifies when a server connection can be reused by other clients. .INDENT 0.0 .TP .B session Server is released back to pool after client disconnects. Default. .TP .B transaction Server is released back to pool after transaction finishes. .TP .B statement Server is released back to pool after query finishes. Long transactions spanning multiple statements are disallowed in this mode. .UNINDENT .SS max_client_conn .sp Maximum number of client connections allowed. When increased then the file descriptor limits should also be increased. Note that actual number of file descriptors used is more than max_client_conn. Theoretical maximum used is: .sp .nf .ft C max_client_conn + (max_pool_size * total_databases * total_users) .ft P .fi .sp if each user connects under its own username to server. If a database user is specified in connect string (all users connect under same username), the theoretical maximum is: .sp .nf .ft C max_client_conn + (max_pool_size * total_databases) .ft P .fi .sp The theoretical maximum should be never reached, unless somebody deliberately crafts special load for it. Still, it means you should set the number of file descriptors to a safely high number. .sp Search for \fBulimit\fP in your favourite shell man page. Note: \fBulimit\fP does not apply in a Windows environment. .sp Default: 100 .SS default_pool_size .sp How many server connections to allow per user/database pair. Can be overridden in the per\-database configuration. .sp Default: 20 .SS min_pool_size .sp Add more server connections to pool if below this number. Improves behaviour when usual load comes suddenly back after period of total inactivity. .sp Default: 0 (disabled) .SS reserve_pool_size .sp How many additional connections to allow to a pool. 0 disables. .sp Default: 0 (disabled) .SS reserve_pool_timeout .sp If a client has not been serviced in this many seconds, pgbouncer enables use of additional connections from reserve pool. 0 disables. .sp Default: 5.0 .SS max_db_connections .sp Do not allow more than this many connections per\-database (regardless of pool \- i.e. user). It should be noted that when you hit the limit, closing a client connection to one pool will not immediately allow a server connection to be established for another pool, because the server connection for the first pool is still open. Once the server connection closes (due to idle timeout), a new server connection will immediately be opened for the waiting pool. .sp Default: unlimited .SS max_user_connections .sp Do not allow more than this many connections per\-user (regardless of pool \- i.e. user). It should be noted that when you hit the limit, closing a client connection to one pool will not immediately allow a server connection to be established for another pool, because the server connection for the first pool is still open. Once the server connection closes (due to idle timeout), a new server connection will immediately be opened for the waiting pool. .SS server_round_robin .sp By default, pgbouncer reuses server connections in LIFO (last\-in, first\-out) manner, so that few connections get the most load. This gives best performance if you have a single server serving a database. But if there is TCP round\-robin behind a database IP, then it is better if pgbouncer also uses connections in that manner, thus achieving uniform load. .sp Default: 0 .SS ignore_startup_parameters .sp By default, PgBouncer allows only parameters it can keep track of in startup packets \- \fBclient_encoding\fP, \fBdatestyle\fP, \fBtimezone\fP and \fBstandard_conforming_strings\fP. .sp All others parameters will raise an error. To allow others parameters, they can be specified here, so that pgbouncer knows that they are handled by admin and it can ignore them. .sp Default: empty .SS disable_pqexec .sp Disable Simple Query protocol (PQexec). Unlike Extended Query protocol, Simple Query allows multiple queries in one packet, which allows some classes of SQL\-injection attacks. Disabling it can improve security. Obviously this means only clients that exclusively use Extended Query protocol will stay working. .sp Default: 0 .SS application_name_add_host .sp Add the client host address and port to the application name setting set on connection start. This helps in identifying the source of bad queries etc. This logic applies only on start of connection, if application_name is later changed with SET, pgbouncer does not change it again. .sp Default: 0 .SS conffile .sp Show location of current config file. Changing it will make PgBouncer use another config file for next \fBRELOAD\fP / \fBSIGHUP\fP. .sp Default: file from command line. .SS service_name .sp Used on win32 service registration. .sp Default: pgbouncer .SS job_name .sp Alias for \fI\%service_name\fP. .SH LOG SETTINGS .SS syslog .sp Toggles syslog on/off As for windows environment, eventlog is used instead. .sp Default: 0 .SS syslog_ident .sp Under what name to send logs to syslog. .sp Default: pgbouncer (program name) .SS syslog_facility .sp Under what facility to send logs to syslog. Possibilities: \fBauth\fP, \fBauthpriv\fP, \fBdaemon\fP, \fBuser\fP, \fBlocal0\-7\fP. .sp Default: daemon .SS log_connections .sp Log successful logins. .sp Default: 1 .SS log_disconnections .sp Log disconnections with reasons. .sp Default: 1 .SS log_pooler_errors .sp Log error messages pooler sends to clients. .sp Default: 1 .SS stats_period .sp Period for writing aggregated stats into log. .sp Default: 60 .SS verbose .sp Increase verbosity. Mirrors "\-v" switch on command line. Using "\-v \-v" on command line is same as \fIverbose=2\fP in config. .sp Default: 0 .SH CONSOLE ACCESS CONTROL .SS admin_users .sp Comma\-separated list of database users that are allowed to connect and run all commands on console. Ignored when \fI\%auth_type\fP is \fBany\fP, in which case any username is allowed in as admin. .sp Default: empty .SS stats_users .sp Comma\-separated list of database users that are allowed to connect and run read\-only queries on console. Thats means all SHOW commands except SHOW FDS. .sp Default: empty. .SH CONNECTION SANITY CHECKS, TIMEOUTS .SS server_reset_query .sp Query sent to server on connection release, before making it available to other clients. At that moment no transaction is in progress so it should not include \fBABORT\fP or \fBROLLBACK\fP. .sp A good choice for Postgres 8.2 and below is: .sp .nf .ft C server_reset_query = RESET ALL; SET SESSION AUTHORIZATION DEFAULT; .ft P .fi .sp for 8.3 and above its enough to do: .sp .nf .ft C server_reset_query = DISCARD ALL; .ft P .fi .sp When transaction pooling is used, the \fI\%server_reset_query\fP should be empty, as clients should not use any session features. If client does use session features, then they will be broken as transaction pooling will not guarantee that next query will be run on same connection. .sp Default: DISCARD ALL .SS server_reset_query_always .sp Whether \fI\%server_reset_query\fP should be run in all pooling modes. When this setting is off (default), the \fI\%server_reset_query\fP will be run only in pools that are in sessions\-pooling mode. Connections in transaction\-pooling mode should not have any need for reset query. .sp Default: 0 .SS server_check_delay .sp How long to keep released connections available for immediate re\-use, without running sanity\-check queries on it. If 0 then the query is ran always. .sp Default: 30.0 .SS server_check_query .sp Simple do\-nothing query to check if the server connection is alive. .sp If an empty string, then sanity checking is disabled. .sp Default: SELECT 1; .SS server_lifetime .sp The pooler will try to close server connections that have been connected longer than this. Setting it to 0 means the connection is to be used only once, then closed. [seconds] .sp Default: 3600.0 .SS server_idle_timeout .sp If a server connection has been idle more than this many seconds it will be dropped. If 0 then timeout is disabled. [seconds] .sp Default: 600.0 .SS server_connect_timeout .sp If connection and login won\(aqt finish in this amount of time, the connection will be closed. [seconds] .sp Default: 15.0 .SS server_login_retry .sp If login failed, because of failure from connect() or authentication that pooler waits this much before retrying to connect. [seconds] .sp Default: 15.0 .SS client_login_timeout .sp If a client connects but does not manage to login in this amount of time, it will be disconnected. Mainly needed to avoid dead connections stalling SUSPEND and thus online restart. [seconds] .sp Default: 60.0 .SS autodb_idle_timeout .sp If the automatically created (via "*") database pools have been unused this many seconds, they are freed. The negative aspect of that is that their statistics are also forgotten. [seconds] .sp Default: 3600.0 .SS dns_max_ttl .sp How long the DNS lookups can be cached. If a DNS lookup returns several answers, pgbouncer will robin\-between them in the meantime. Actual DNS TTL is ignored. [seconds] .sp Default: 15.0 .SS dns_nxdomain_ttl .sp How long error and NXDOMAIN DNS lookups can be cached. [seconds] .sp Default: 15.0 .SS dns_zone_check_period .sp Period to check if zone serial has changed. .sp PgBouncer can collect dns zones from hostnames (everything after first dot) and then periodically check if zone serial changes. If it notices changes, all hostnames under that zone are looked up again. If any host ip changes, it\(aqs connections are invalidated. .sp Works only with UDNS backend (\fB\-\-with\-udns\fP to configure). .sp Default: 0.0 (disabled) .SH TLS SETTINGS .SS client_tls_sslmode .sp TLS mode to use for connections from clients. TLS connections are disabled by default. When enabled, \fI\%client_tls_key_file\fP and \fI\%client_tls_cert_file\fP must be also configured to set up key and cert PgBouncer uses to accept client connections. .INDENT 0.0 .TP .B disabled Plain TCP. If client requests TLS, it\(aqs ignored. Default. .TP .B allow If client requests TLS, it is used. If not, plain TCP is used. If client uses client\-certificate, it is not validated. .TP .B prefer Same as \fBallow\fP. .TP .B require Client must use TLS. If not, client connection is rejected. If client uses client\-certificate, it is not validated. .TP .B verify\-ca Client must use TLS with valid client certificate. .TP .B verify\-full Same as \fBverify\-ca\fP. .UNINDENT .SS client_tls_key_file .sp Private key for PgBouncer to accept client connections. .sp Default: not set. .SS client_tls_cert_file .sp Certificate for private key. Clients can validate it. .sp Default: not set. .SS client_tls_ca_file .sp Root certificate file to validate client certificates. .sp Default: unset. .SS client_tls_protocols .sp Which TLS protocol versions are allowed. Allowed values: \fBtlsv1.0\fP, \fBtlsv1.1\fP, \fBtlsv1.2\fP. Shortcuts: \fBall\fP (tlsv1.0,tlsv1.1,tlsv1.2), \fBsecure\fP (tlsv1.2), \fBlegacy\fP (all). .sp Default: \fBall\fP .SS client_tls_ciphers .sp Default: \fBfast\fP .SS client_tls_ecdhcurve .sp Elliptic Curve name to use for ECDH key exchanges. .sp Allowed values: \fBnone\fP (DH is disabled), \fBauto\fP (256\-bit ECDH), curve name. .sp Default: \fBauto\fP .SS client_tls_dheparams .sp DHE key exchange type. .sp Allowed values: \fBnone\fP (DH is disabled), \fBauto\fP (2048\-bit DH), \fBlegacy\fP (1024\-bit DH). .sp Default: \fBauto\fP .SS server_tls_sslmode .sp TLS mode to use for connections to PostgreSQL servers. TLS connections are disabled by default. .INDENT 0.0 .TP .B disabled Plain TCP. TCP is not event requested from server. Default. .TP .B allow FIXME: if server rejects plain, try TLS? .TP .B prefer TLS connection is always requested first from PostgreSQL, when refused connection will be establised over plain TCP. Server certificate is not validated. .TP .B require Connection must go over TLS. If server rejects it, plain TCP is not attempted. Server certificate is not validated. .TP .B verify\-ca Connection must go over TLS and server certificate must be valid according to \fI\%server_tls_ca_file\fP. Server hostname is not checked against certificate. .TP .B verify\-full Connection must go over TLS and server certificate must be valid according to \fI\%server_tls_ca_file\fP. Server hostname must match certificate info. .UNINDENT .SS server_tls_ca_file .sp Root certificate file to validate PostgreSQL server certificates. .sp Default: unset. .SS server_tls_key_file .sp Private key for PgBouncer to authenticate against PostgreSQL server. .sp Default: not set. .SS server_tls_cert_file .sp Certificate for private key. PostgreSQL server can validate it. .sp Default: not set. .SS server_tls_protocols .sp Which TLS protocol versions are allowed. Allowed values: \fBtlsv1.0\fP, \fBtlsv1.1\fP, \fBtlsv1.2\fP. Shortcuts: \fBall\fP (tlsv1.0,tlsv1.1,tlsv1.2), \fBsecure\fP (tlsv1.2), \fBlegacy\fP (all). .sp Default: \fBall\fP .SS server_tls_ciphers .sp Default: \fBfast\fP .SH DANGEROUS TIMEOUTS .sp Setting following timeouts cause unexpected errors. .SS query_timeout .sp Queries running longer than that are canceled. This should be used only with slightly smaller server\-side statement_timeout, to apply only for network problems. [seconds] .sp Default: 0.0 (disabled) .SS query_wait_timeout .sp Maximum time queries are allowed to spend waiting for execution. If the query is not assigned to a server during that time, the client is disconnected. This is used to prevent unresponsive servers from grabbing up connections. [seconds] .sp It also helps when server is down or database rejects connections for any reason. If this is disabled, clients will be queued infinitely. .sp Default: 120 .SS client_idle_timeout .sp Client connections idling longer than this many seconds are closed. This should be larger than the client\-side connection lifetime settings, and only used for network problems. [seconds] .sp Default: 0.0 (disabled) .SS idle_transaction_timeout .sp If client has been in "idle in transaction" state longer, it will be disconnected. [seconds] .sp Default: 0.0 (disabled) .SH LOW-LEVEL NETWORK SETTINGS .SS pkt_buf .sp Internal buffer size for packets. Affects size of TCP packets sent and general memory usage. Actual libpq packets can be larger than this so, no need to set it large. .sp Default: 4096 .SS max_packet_size .sp Maximum size for Postgres packets that PgBouncer allows through. One packet is either one query or one resultset row. Full resultset can be larger. .sp Default: 2147483647 .SS listen_backlog .sp Backlog argument for listen(2). Determines how many new unanswered connection attempts are kept in queue. When queue is full, further new connections are dropped. .sp Default: 128 .SS sbuf_loopcnt .sp How many times to process data on one connection, before proceeding. Without this limit, one connection with a big resultset can stall PgBouncer for a long time. One loop processes one \fI\%pkt_buf\fP amount of data. 0 means no limit. .sp Default: 5 .SS suspend_timeout .sp How many seconds to wait for buffer flush during SUSPEND or reboot (\-R). Connection is dropped if flush does not succeed. .sp Default: 10 .SS tcp_defer_accept .sp For details on this and other tcp options, please see \fBman 7 tcp\fP. .sp Default: 45 on Linux, otherwise 0 .SS tcp_socket_buffer .sp Default: not set .SS tcp_keepalive .sp Turns on basic keepalive with OS defaults. .sp On Linux, the system defaults are \fBtcp_keepidle=7200\fP, \fBtcp_keepintvl=75\fP, \fBtcp_keepcnt=9\fP. They are probably similar on other OS\-es. .sp Default: 1 .SS tcp_keepcnt .sp Default: not set .SS tcp_keepidle .sp Default: not set .SS tcp_keepintvl .sp Default: not set .SH SECTION [DATABASES] .sp This contains key=value pairs where key will be taken as a database name and value as a libpq connect\-string style list of key=value pairs. As actual libpq is not used, so not all features from libpq can be used (service=, .pgpass). .sp Database name can contain characters \fB_0\-9A\-Za\-z\fP without quoting. Names that contain other chars need to be quoted with standard SQL ident quoting: double quotes where "" is taken as single quote. .sp "*" acts as fallback database: if the exact name does not exist, its value is taken as connect string for requested database. Such automatically created database entries are cleaned up if they stay idle longer then the time specified in \fI\%autodb_idle_timeout\fP parameter. .SS dbname .sp Destination database name. .sp Default: same as client\-side database name. .SS host .sp Hostname or IP address to connect to. Hostnames are resolved on connect time, the result is cached per \fBdns_max_ttl\fP parameter. If DNS returns several results, they are used in round\-robin manner. .sp Default: not set, meaning to use a Unix socket. .SS port .sp Default: 5432 .SS user, password .sp If \fBuser=\fP is set, all connections to the destination database will be done with the specified user, meaning that there will be only one pool for this database. .sp Otherwise PgBouncer tries to log into the destination database with client username, meaning that there will be one pool per user. .SS auth_user .sp If \fBauth_user\fP is set, any user not specified in auth_file will be queried from pg_shadow in the database using auth_user. Auth_user\(aqs password will be taken from auth_file. .SS pool_size .sp Set maximum size of pools for this database. If not set, the default_pool_size is used. .SS connect_query .sp Query to be executed after a connection is established, but before allowing the connection to be used by any clients. If the query raises errors, they are logged but ignored otherwise. .SS pool_mode .sp Set the pool mode specific to this database. If not set, the default pool_mode is used. .SS max_db_connections .sp Configure a database\-wide maximum (i.e. all pools within the database will not have more than this many server connections). .SS client_encoding .sp Ask specific \fBclient_encoding\fP from server. .SS datestyle .sp Ask specific \fBdatestyle\fP from server. .SS timezone .sp Ask specific \fBtimezone\fP from server. .SH SECTION [USERS] .sp This contains key=value pairs where key will be taken as a user name and value as a libpq connect\-string style list of key=value pairs. As actual libpq is not used, so not all features from libpq can be used. .SS pool_mode .sp Set the pool mode to be used for all connections from this user. If not set, the database or default pool_mode is used. .SH INCLUDE DIRECTIVE .sp The PgBouncer config file can contain include directives, which specify another config file to read and process. This allows for splitting the configuration file into physically separate parts. The include directives look like this: .sp .nf .ft C %include filename .ft P .fi .sp If the file name is not absolute path it is taken as relative to current working directory. .SH AUTHENTICATION FILE FORMAT .sp PgBouncer needs its own user database. The users are loaded from a text file in following format: .sp .nf .ft C "username1" "password" ... "username2" "md5abcdef012342345" ... .ft P .fi .sp There should be at least 2 fields, surrounded by double quotes. The first field is the username and the second is either a plain\-text or a MD5\-hidden password. PgBouncer ignores the rest of the line. .sp This file format is equivalent to text files used by PostgreSQL 8.x for authentication info, thus allowing PgBouncer to work directly on PostgreSQL authentication files in data directory. .sp Since PostgreSQL 9.0, the text files are not used anymore. Thus the auth file needs to be generated. See \fI./etc/mkauth.py\fP for sample script to generate auth file from \fIpg_shadow\fP table. .sp PostgreSQL MD5\-hidden password format: .sp .nf .ft C "md5" + md5(password + username) .ft P .fi .sp So user \fIadmin\fP with password \fI1234\fP will have MD5\-hidden password \fImd545f2603610af569b6155c45067268c6b\fP. .SH HBA FILE FORMAT .sp It follows the format of PostgreSQL pg_hba.conf file \- \fI\%http://www.postgresql.org/docs/9.4/static/auth\-pg\-hba\-conf.html\fP .sp There are following differences: .INDENT 0.0 .IP \(bu 2 Supported record types: \fIlocal\fP, \fIhost\fP, \fIhostssl\fP, \fIhostnossl\fP. .IP \(bu 2 Database field: Supports \fIall\fP, \fIsameuser\fP, \fI@file\fP, multiple names. Not supported: \fIreplication\fP, \fIsamerole\fP, \fIsamegroup\fP. .IP \(bu 2 Username field: Supports \fIall\fP, \fI@file\fP, multiple names. Not supported: \fI+groupname\fP. .IP \(bu 2 Address field: Supported IPv4, IPv6. Not supported: DNS names, domain prefixes. .IP \(bu 2 Auth\-method field: Supported methods: \fItrust\fP, \fIreject\fP, \fImd5\fP, \fIpassword\fP, \fIpeer\fP, \fIcert\fP. Not supported: \fIgss\fP, \fIsspi\fP, \fIident\fP, \fIldap\fP, \fIradius\fP, \fIpam\fP. Also username map (\fImap=\fP) parameter is not supported. .UNINDENT .SH EXAMPLE .sp Minimal config: .sp .nf .ft C [databases] template1 = host=127.0.0.1 dbname=template1 auth_user=someuser [pgbouncer] pool_mode = session listen_port = 6543 listen_addr = 127.0.0.1 auth_type = md5 auth_file = users.txt logfile = pgbouncer.log pidfile = pgbouncer.pid admin_users = someuser stats_users = stat_collector .ft P .fi .sp Database defaults: .sp .nf .ft C [databases] ; foodb over unix socket foodb = ; redirect bardb to bazdb on localhost bardb = host=127.0.0.1 dbname=bazdb ; access to destination database will go with single user forcedb = host=127.0.0.1 port=300 user=baz password=foo client_encoding=UNICODE datestyle=ISO .ft P .fi .SH SEE ALSO .sp \fI\%https://pgbouncer.github.io/\fP .sp \fI\%https://wiki.postgresql.org/wiki/PgBouncer\fP .sp pgbouncer(1) \- manpage for general usage, console commands. .\" Generated by docutils manpage writer. .\" . pgbouncer-1.7/doc/frag-config-man0000664000175000017500000000026312572127367013730 00000000000000-------------------------------- configuration file for pgbouncer -------------------------------- :Date: 2006-10-23 :Version: 1.7 :Manual section: 5 :Manual group: Databases pgbouncer-1.7/doc/config.rst0000664000175000017500000006115612617620325013051 00000000000000 pgbouncer.ini ############# Description =========== Config file is in "ini" format. Section names are between "[" and "]". Lines starting with ";" or "#" are taken as comments and ignored. The characters ";" and "#" are not recognized when they appear later in the line. Generic settings ================ logfile ------- Specifies log file. Log file is kept open so after rotation ``kill -HUP`` or on console ``RELOAD;`` should be done. Note: On Windows machines, the service must be stopped and started. Default: not set. pidfile ------- Specifies the pid file. Without a pidfile, daemonization is not allowed. Default: not set. listen_addr ----------- Specifies list of addresses, where to listen for TCP connections. You may also use ``*`` meaning "listen on all addresses". When not set, only Unix socket connections are allowed. Addresses can be specified numerically (IPv4/IPv6) or by name. Default: not set listen_port ----------- Which port to listen on. Applies to both TCP and Unix sockets. Default: 6432 unix_socket_dir --------------- Specifies location for Unix sockets. Applies to both listening socket and server connections. If set to an empty string, Unix sockets are disabled. Required for online reboot (-R) to work. Note: Not supported on Windows machines. Default: /tmp unix_socket_mode ---------------- Filesystem mode for unix socket. Default: 0777 unix_socket_group ----------------- Group name to use for unix socket. Default: not set user ---- If set, specifies the Unix user to change to after startup. Works only if PgBouncer is started as root or if it's already running as given user. Note: Not supported on Windows machines. Default: not set auth_file --------- The name of the file to load user names and passwords from. The file format is the same as the PostgreSQL 8.x pg_auth/pg_pwd file, so this setting can be pointed directly to one of those backend files. Since version 9.0, PostgreSQL does not use such text file, so it must be generated manually. See section `Authentication file format`_ below about details. Default: not set. auth_hba_file ------------- HBA configuration file to use when `auth_type`_ is ``hba``. Supported from version 1.7 onwards. Default: not set auth_type --------- How to authenticate users. hba Actual auth type is loaded from `auth_hba_file`_. This allows different authentication methods different access paths. Example: connection over unix socket use ``peer`` auth method, connection over TCP must use TLS. Supported from version 1.7 onwards. cert Client must connect over TLS connection with valid client cert. Username is then taken from CommonName field from certificate. md5 Use MD5-based password check. `auth_file`_ may contain both MD5-encrypted or plain-text passwords. This is the default authentication method. plain Clear-text password is sent over wire. Deprecated. trust No authentication is done. Username must still exist in `auth_file`_. any Like the ``trust`` method, but the username given is ignored. Requires that all databases are configured to log in as specific user. Additionally, the console database allows any user to log in as admin. auth_query ---------- Query to load user's password from db. Default: ``SELECT usename, passwd FROM pg_shadow WHERE usename=$1`` pool_mode --------- Specifies when a server connection can be reused by other clients. session Server is released back to pool after client disconnects. Default. transaction Server is released back to pool after transaction finishes. statement Server is released back to pool after query finishes. Long transactions spanning multiple statements are disallowed in this mode. max_client_conn --------------- Maximum number of client connections allowed. When increased then the file descriptor limits should also be increased. Note that actual number of file descriptors used is more than max_client_conn. Theoretical maximum used is:: max_client_conn + (max_pool_size * total_databases * total_users) if each user connects under its own username to server. If a database user is specified in connect string (all users connect under same username), the theoretical maximum is:: max_client_conn + (max_pool_size * total_databases) The theoretical maximum should be never reached, unless somebody deliberately crafts special load for it. Still, it means you should set the number of file descriptors to a safely high number. Search for ``ulimit`` in your favourite shell man page. Note: ``ulimit`` does not apply in a Windows environment. Default: 100 default_pool_size ----------------- How many server connections to allow per user/database pair. Can be overridden in the per-database configuration. Default: 20 min_pool_size ------------- Add more server connections to pool if below this number. Improves behaviour when usual load comes suddenly back after period of total inactivity. Default: 0 (disabled) reserve_pool_size ----------------- How many additional connections to allow to a pool. 0 disables. Default: 0 (disabled) reserve_pool_timeout -------------------- If a client has not been serviced in this many seconds, pgbouncer enables use of additional connections from reserve pool. 0 disables. Default: 5.0 max_db_connections ------------------ Do not allow more than this many connections per-database (regardless of pool - i.e. user). It should be noted that when you hit the limit, closing a client connection to one pool will not immediately allow a server connection to be established for another pool, because the server connection for the first pool is still open. Once the server connection closes (due to idle timeout), a new server connection will immediately be opened for the waiting pool. Default: unlimited max_user_connections -------------------- Do not allow more than this many connections per-user (regardless of pool - i.e. user). It should be noted that when you hit the limit, closing a client connection to one pool will not immediately allow a server connection to be established for another pool, because the server connection for the first pool is still open. Once the server connection closes (due to idle timeout), a new server connection will immediately be opened for the waiting pool. server_round_robin ------------------ By default, pgbouncer reuses server connections in LIFO (last-in, first-out) manner, so that few connections get the most load. This gives best performance if you have a single server serving a database. But if there is TCP round-robin behind a database IP, then it is better if pgbouncer also uses connections in that manner, thus achieving uniform load. Default: 0 ignore_startup_parameters ------------------------- By default, PgBouncer allows only parameters it can keep track of in startup packets - ``client_encoding``, ``datestyle``, ``timezone`` and ``standard_conforming_strings``. All others parameters will raise an error. To allow others parameters, they can be specified here, so that pgbouncer knows that they are handled by admin and it can ignore them. Default: empty disable_pqexec -------------- Disable Simple Query protocol (PQexec). Unlike Extended Query protocol, Simple Query allows multiple queries in one packet, which allows some classes of SQL-injection attacks. Disabling it can improve security. Obviously this means only clients that exclusively use Extended Query protocol will stay working. Default: 0 application_name_add_host ------------------------- Add the client host address and port to the application name setting set on connection start. This helps in identifying the source of bad queries etc. This logic applies only on start of connection, if application_name is later changed with SET, pgbouncer does not change it again. Default: 0 conffile -------- Show location of current config file. Changing it will make PgBouncer use another config file for next ``RELOAD`` / ``SIGHUP``. Default: file from command line. service_name ------------ Used on win32 service registration. Default: pgbouncer job_name -------- Alias for `service_name`_. Log settings ============ syslog ------ Toggles syslog on/off As for windows environment, eventlog is used instead. Default: 0 syslog_ident ------------ Under what name to send logs to syslog. Default: pgbouncer (program name) syslog_facility --------------- Under what facility to send logs to syslog. Possibilities: ``auth``, ``authpriv``, ``daemon``, ``user``, ``local0-7``. Default: daemon log_connections --------------- Log successful logins. Default: 1 log_disconnections ------------------ Log disconnections with reasons. Default: 1 log_pooler_errors ----------------- Log error messages pooler sends to clients. Default: 1 stats_period ------------ Period for writing aggregated stats into log. Default: 60 verbose ------- Increase verbosity. Mirrors "-v" switch on command line. Using "-v -v" on command line is same as `verbose=2` in config. Default: 0 Console access control ====================== admin_users ----------- Comma-separated list of database users that are allowed to connect and run all commands on console. Ignored when `auth_type`_ is ``any``, in which case any username is allowed in as admin. Default: empty stats_users ----------- Comma-separated list of database users that are allowed to connect and run read-only queries on console. Thats means all SHOW commands except SHOW FDS. Default: empty. Connection sanity checks, timeouts ================================== server_reset_query ------------------ Query sent to server on connection release, before making it available to other clients. At that moment no transaction is in progress so it should not include ``ABORT`` or ``ROLLBACK``. A good choice for Postgres 8.2 and below is:: server_reset_query = RESET ALL; SET SESSION AUTHORIZATION DEFAULT; for 8.3 and above its enough to do:: server_reset_query = DISCARD ALL; When transaction pooling is used, the `server_reset_query`_ should be empty, as clients should not use any session features. If client does use session features, then they will be broken as transaction pooling will not guarantee that next query will be run on same connection. Default: DISCARD ALL server_reset_query_always ------------------------- Whether `server_reset_query`_ should be run in all pooling modes. When this setting is off (default), the `server_reset_query`_ will be run only in pools that are in sessions-pooling mode. Connections in transaction-pooling mode should not have any need for reset query. Default: 0 server_check_delay ------------------ How long to keep released connections available for immediate re-use, without running sanity-check queries on it. If 0 then the query is ran always. Default: 30.0 server_check_query ------------------ Simple do-nothing query to check if the server connection is alive. If an empty string, then sanity checking is disabled. Default: SELECT 1; server_lifetime --------------- The pooler will try to close server connections that have been connected longer than this. Setting it to 0 means the connection is to be used only once, then closed. [seconds] Default: 3600.0 server_idle_timeout ------------------- If a server connection has been idle more than this many seconds it will be dropped. If 0 then timeout is disabled. [seconds] Default: 600.0 server_connect_timeout ---------------------- If connection and login won't finish in this amount of time, the connection will be closed. [seconds] Default: 15.0 server_login_retry ------------------ If login failed, because of failure from connect() or authentication that pooler waits this much before retrying to connect. [seconds] Default: 15.0 client_login_timeout -------------------- If a client connects but does not manage to login in this amount of time, it will be disconnected. Mainly needed to avoid dead connections stalling SUSPEND and thus online restart. [seconds] Default: 60.0 autodb_idle_timeout ------------------- If the automatically created (via "*") database pools have been unused this many seconds, they are freed. The negative aspect of that is that their statistics are also forgotten. [seconds] Default: 3600.0 dns_max_ttl ----------- How long the DNS lookups can be cached. If a DNS lookup returns several answers, pgbouncer will robin-between them in the meantime. Actual DNS TTL is ignored. [seconds] Default: 15.0 dns_nxdomain_ttl ---------------- How long error and NXDOMAIN DNS lookups can be cached. [seconds] Default: 15.0 dns_zone_check_period --------------------- Period to check if zone serial has changed. PgBouncer can collect dns zones from hostnames (everything after first dot) and then periodically check if zone serial changes. If it notices changes, all hostnames under that zone are looked up again. If any host ip changes, it's connections are invalidated. Works only with UDNS backend (``--with-udns`` to configure). Default: 0.0 (disabled) TLS settings ============ client_tls_sslmode ------------------ TLS mode to use for connections from clients. TLS connections are disabled by default. When enabled, `client_tls_key_file`_ and `client_tls_cert_file`_ must be also configured to set up key and cert PgBouncer uses to accept client connections. disabled Plain TCP. If client requests TLS, it's ignored. Default. allow If client requests TLS, it is used. If not, plain TCP is used. If client uses client-certificate, it is not validated. prefer Same as ``allow``. require Client must use TLS. If not, client connection is rejected. If client uses client-certificate, it is not validated. verify-ca Client must use TLS with valid client certificate. verify-full Same as ``verify-ca``. client_tls_key_file ------------------- Private key for PgBouncer to accept client connections. Default: not set. client_tls_cert_file -------------------- Certificate for private key. Clients can validate it. Default: not set. client_tls_ca_file ------------------ Root certificate file to validate client certificates. Default: unset. client_tls_protocols -------------------- Which TLS protocol versions are allowed. Allowed values: ``tlsv1.0``, ``tlsv1.1``, ``tlsv1.2``. Shortcuts: ``all`` (tlsv1.0,tlsv1.1,tlsv1.2), ``secure`` (tlsv1.2), ``legacy`` (all). Default: ``all`` client_tls_ciphers ------------------ Default: ``fast`` client_tls_ecdhcurve -------------------- Elliptic Curve name to use for ECDH key exchanges. Allowed values: ``none`` (DH is disabled), ``auto`` (256-bit ECDH), curve name. Default: ``auto`` client_tls_dheparams -------------------- DHE key exchange type. Allowed values: ``none`` (DH is disabled), ``auto`` (2048-bit DH), ``legacy`` (1024-bit DH). Default: ``auto`` server_tls_sslmode ------------------ TLS mode to use for connections to PostgreSQL servers. TLS connections are disabled by default. disabled Plain TCP. TCP is not event requested from server. Default. allow FIXME: if server rejects plain, try TLS? prefer TLS connection is always requested first from PostgreSQL, when refused connection will be establised over plain TCP. Server certificate is not validated. require Connection must go over TLS. If server rejects it, plain TCP is not attempted. Server certificate is not validated. verify-ca Connection must go over TLS and server certificate must be valid according to `server_tls_ca_file`_. Server hostname is not checked against certificate. verify-full Connection must go over TLS and server certificate must be valid according to `server_tls_ca_file`_. Server hostname must match certificate info. server_tls_ca_file ------------------ Root certificate file to validate PostgreSQL server certificates. Default: unset. server_tls_key_file ------------------- Private key for PgBouncer to authenticate against PostgreSQL server. Default: not set. server_tls_cert_file -------------------- Certificate for private key. PostgreSQL server can validate it. Default: not set. server_tls_protocols -------------------- Which TLS protocol versions are allowed. Allowed values: ``tlsv1.0``, ``tlsv1.1``, ``tlsv1.2``. Shortcuts: ``all`` (tlsv1.0,tlsv1.1,tlsv1.2), ``secure`` (tlsv1.2), ``legacy`` (all). Default: ``all`` server_tls_ciphers ------------------ Default: ``fast`` Dangerous timeouts ================== Setting following timeouts cause unexpected errors. query_timeout ------------- Queries running longer than that are canceled. This should be used only with slightly smaller server-side statement_timeout, to apply only for network problems. [seconds] Default: 0.0 (disabled) query_wait_timeout ------------------ Maximum time queries are allowed to spend waiting for execution. If the query is not assigned to a server during that time, the client is disconnected. This is used to prevent unresponsive servers from grabbing up connections. [seconds] It also helps when server is down or database rejects connections for any reason. If this is disabled, clients will be queued infinitely. Default: 120 client_idle_timeout ------------------- Client connections idling longer than this many seconds are closed. This should be larger than the client-side connection lifetime settings, and only used for network problems. [seconds] Default: 0.0 (disabled) idle_transaction_timeout ------------------------ If client has been in "idle in transaction" state longer, it will be disconnected. [seconds] Default: 0.0 (disabled) Low-level network settings ========================== pkt_buf ------- Internal buffer size for packets. Affects size of TCP packets sent and general memory usage. Actual libpq packets can be larger than this so, no need to set it large. Default: 4096 max_packet_size --------------- Maximum size for Postgres packets that PgBouncer allows through. One packet is either one query or one resultset row. Full resultset can be larger. Default: 2147483647 listen_backlog -------------- Backlog argument for listen(2). Determines how many new unanswered connection attempts are kept in queue. When queue is full, further new connections are dropped. Default: 128 sbuf_loopcnt ------------ How many times to process data on one connection, before proceeding. Without this limit, one connection with a big resultset can stall PgBouncer for a long time. One loop processes one `pkt_buf`_ amount of data. 0 means no limit. Default: 5 suspend_timeout --------------- How many seconds to wait for buffer flush during SUSPEND or reboot (-R). Connection is dropped if flush does not succeed. Default: 10 tcp_defer_accept ---------------- For details on this and other tcp options, please see ``man 7 tcp``. Default: 45 on Linux, otherwise 0 tcp_socket_buffer ----------------- Default: not set tcp_keepalive -------------- Turns on basic keepalive with OS defaults. On Linux, the system defaults are **tcp_keepidle=7200**, **tcp_keepintvl=75**, **tcp_keepcnt=9**. They are probably similar on other OS-es. Default: 1 tcp_keepcnt ----------- Default: not set tcp_keepidle ------------ Default: not set tcp_keepintvl ------------- Default: not set Section [databases] =================== This contains key=value pairs where key will be taken as a database name and value as a libpq connect-string style list of key=value pairs. As actual libpq is not used, so not all features from libpq can be used (service=, .pgpass). Database name can contain characters ``_0-9A-Za-z`` without quoting. Names that contain other chars need to be quoted with standard SQL ident quoting: double quotes where "" is taken as single quote. "*" acts as fallback database: if the exact name does not exist, its value is taken as connect string for requested database. Such automatically created database entries are cleaned up if they stay idle longer then the time specified in `autodb_idle_timeout`_ parameter. dbname ------ Destination database name. Default: same as client-side database name. host ---- Hostname or IP address to connect to. Hostnames are resolved on connect time, the result is cached per ``dns_max_ttl`` parameter. If DNS returns several results, they are used in round-robin manner. Default: not set, meaning to use a Unix socket. port ---- Default: 5432 user, password -------------- If ``user=`` is set, all connections to the destination database will be done with the specified user, meaning that there will be only one pool for this database. Otherwise PgBouncer tries to log into the destination database with client username, meaning that there will be one pool per user. auth_user --------- If ``auth_user`` is set, any user not specified in auth_file will be queried from pg_shadow in the database using auth_user. Auth_user's password will be taken from auth_file. pool_size --------- Set maximum size of pools for this database. If not set, the default_pool_size is used. connect_query ------------- Query to be executed after a connection is established, but before allowing the connection to be used by any clients. If the query raises errors, they are logged but ignored otherwise. pool_mode --------- Set the pool mode specific to this database. If not set, the default pool_mode is used. max_db_connections ------------------ Configure a database-wide maximum (i.e. all pools within the database will not have more than this many server connections). client_encoding --------------- Ask specific ``client_encoding`` from server. datestyle --------- Ask specific ``datestyle`` from server. timezone -------- Ask specific **timezone** from server. Section [users] =============== This contains key=value pairs where key will be taken as a user name and value as a libpq connect-string style list of key=value pairs. As actual libpq is not used, so not all features from libpq can be used. pool_mode --------- Set the pool mode to be used for all connections from this user. If not set, the database or default pool_mode is used. Include directive ================= The PgBouncer config file can contain include directives, which specify another config file to read and process. This allows for splitting the configuration file into physically separate parts. The include directives look like this:: %include filename If the file name is not absolute path it is taken as relative to current working directory. Authentication file format ========================== PgBouncer needs its own user database. The users are loaded from a text file in following format:: "username1" "password" ... "username2" "md5abcdef012342345" ... There should be at least 2 fields, surrounded by double quotes. The first field is the username and the second is either a plain-text or a MD5-hidden password. PgBouncer ignores the rest of the line. This file format is equivalent to text files used by PostgreSQL 8.x for authentication info, thus allowing PgBouncer to work directly on PostgreSQL authentication files in data directory. Since PostgreSQL 9.0, the text files are not used anymore. Thus the auth file needs to be generated. See `./etc/mkauth.py` for sample script to generate auth file from `pg_shadow` table. PostgreSQL MD5-hidden password format:: "md5" + md5(password + username) So user `admin` with password `1234` will have MD5-hidden password `md545f2603610af569b6155c45067268c6b`. HBA file format =============== It follows the format of PostgreSQL pg_hba.conf file - http://www.postgresql.org/docs/9.4/static/auth-pg-hba-conf.html There are following differences: * Supported record types: `local`, `host`, `hostssl`, `hostnossl`. * Database field: Supports `all`, `sameuser`, `@file`, multiple names. Not supported: `replication`, `samerole`, `samegroup`. * Username field: Supports `all`, `@file`, multiple names. Not supported: `+groupname`. * Address field: Supported IPv4, IPv6. Not supported: DNS names, domain prefixes. * Auth-method field: Supported methods: `trust`, `reject`, `md5`, `password`, `peer`, `cert`. Not supported: `gss`, `sspi`, `ident`, `ldap`, `radius`, `pam`. Also username map (`map=`) parameter is not supported. Example ======= Minimal config:: [databases] template1 = host=127.0.0.1 dbname=template1 auth_user=someuser [pgbouncer] pool_mode = session listen_port = 6543 listen_addr = 127.0.0.1 auth_type = md5 auth_file = users.txt logfile = pgbouncer.log pidfile = pgbouncer.pid admin_users = someuser stats_users = stat_collector Database defaults:: [databases] ; foodb over unix socket foodb = ; redirect bardb to bazdb on localhost bardb = host=127.0.0.1 dbname=bazdb ; access to destination database will go with single user forcedb = host=127.0.0.1 port=300 user=baz password=foo client_encoding=UNICODE datestyle=ISO See also ======== https://pgbouncer.github.io/ https://wiki.postgresql.org/wiki/PgBouncer pgbouncer(1) - manpage for general usage, console commands. pgbouncer-1.7/doc/pgbouncer.10000664000175000017500000003356112630310723013110 00000000000000.\" Man page generated from reStructeredText. . .TH PGBOUNCER 5 "2006-10-23" "1.7" "Databases" .SH NAME pgbouncer \- lightweight connection pooler for PostgreSQL . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .SH SYNOPSIS .sp .nf .ft C pgbouncer [\-d][\-R][\-v][\-u user] pgbouncer \-V|\-h .ft P .fi .sp On Windows computers, the options are: .sp .nf .ft C pgbouncer.exe [\-v][\-u user] pgbouncer.exe \-V|\-h .ft P .fi .sp Additional options for setting up a Windows service: .sp .nf .ft C pgbouncer.exe \-\-regservice pgbouncer.exe \-\-unregservice .ft P .fi .SH DESCRIPTION .sp \fBpgbouncer\fP is a PostgreSQL connection pooler. Any target application can be connected to \fBpgbouncer\fP as if it were a PostgreSQL server, and \fBpgbouncer\fP will create a connection to the actual server, or it will reuse one of its existing connections. .sp The aim of \fBpgbouncer\fP is to lower the performance impact of opening new connections to PostgreSQL. .sp In order not to compromise transaction semantics for connection pooling, \fBpgbouncer\fP supports several types of pooling when rotating connections: .INDENT 0.0 .TP .B Session pooling Most polite method. When client connects, a server connection will be assigned to it for the whole duration the client stays connected. When the client disconnects, the server connection will be put back into the pool. This is the default method. .TP .B Transaction pooling A server connection is assigned to client only during a transaction. When PgBouncer notices that transaction is over, the server connection will be put back into the pool. .TP .B Statement pooling Most aggressive method. The server connection will be put back into pool immediately after a query completes. Multi\-statement transactions are disallowed in this mode as they would break. .UNINDENT .sp The administration interface of \fBpgbouncer\fP consists of some new \fBSHOW\fP commands available when connected to a special \(aqvirtual\(aq database \fBpgbouncer\fP. .SH QUICK-START .sp Basic setup and usage as following. .INDENT 0.0 .IP 1. 3 Create a pgbouncer.ini file. Details in \fBpgbouncer(5)\fP. Simple example: .sp .nf .ft C [databases] template1 = host=127.0.0.1 port=5432 dbname=template1 [pgbouncer] listen_port = 6543 listen_addr = 127.0.0.1 auth_type = md5 auth_file = users.txt logfile = pgbouncer.log pidfile = pgbouncer.pid admin_users = someuser .ft P .fi .IP 2. 3 Create \fBusers.txt\fP file that contains users allowed in: .sp .nf .ft C "someuser" "same_password_as_in_server" .ft P .fi .IP 3. 3 Launch \fBpgbouncer\fP: .sp .nf .ft C $ pgbouncer \-d pgbouncer.ini .ft P .fi .IP 4. 3 Have your application (or the \fBpsql\fP client) connect to \fBpgbouncer\fP instead of directly to PostgreSQL server: .sp .nf .ft C $ psql \-p 6543 \-U someuser template1 .ft P .fi .IP 5. 3 Manage \fBpgbouncer\fP by connecting to the special administration database \fBpgbouncer\fP and issuing \fBshow help;\fP to begin: .sp .nf .ft C $ psql \-p 6543 \-U someuser pgbouncer pgbouncer=# show help; NOTICE: Console usage DETAIL: SHOW [HELP|CONFIG|DATABASES|FDS|POOLS|CLIENTS|SERVERS|SOCKETS|LISTS|VERSION] SET key = arg RELOAD PAUSE SUSPEND RESUME SHUTDOWN .ft P .fi .IP 6. 3 If you made changes to the pgbouncer.ini file, you can reload it with: .sp .nf .ft C pgbouncer=# RELOAD; .ft P .fi .UNINDENT .SH COMMAND LINE SWITCHES .INDENT 0.0 .TP .B \-d Run in background. Without it the process will run in foreground. Note: Does not work on Windows, \fBpgbouncer\fP need to run as service there. .TP .B \-R Do an online restart. That means connecting to the running process, loading the open sockets from it, and then using them. If there is no active process, boot normally. Note: Works only if OS supports Unix sockets and the \fIunix_socket_dir\fP is not disabled in config. Does not work on Windows machines. Does not work with TLS connections, they are dropped. .TP .BI \-u \ user Switch to the given user on startup. .TP .B \-v Increase verbosity. Can be used multiple times. .TP .B \-q Be quiet \- do not log to stdout. Note this does not affect logging verbosity, only that stdout is not to be used. For use in init.d scripts. .TP .B \-V Show version. .TP .B \-h Show short help. .TP .B \-\-regservice Win32: Register pgbouncer to run as Windows service. The \fBservice_name\fP config parameter value is used as name to register under. .TP .B \-\-unregservice Win32: Unregister Windows service. .UNINDENT .SH ADMIN CONSOLE .sp The console is available by connecting as normal to the database \fBpgbouncer\fP: .sp .nf .ft C $ psql \-p 6543 pgbouncer .ft P .fi .sp Only users listed in configuration parameters \fBadmin_users\fP or \fBstats_users\fP are allowed to login to the console. (Except when \fIauth_mode=any\fP, then any user is allowed in as a stats_user.) .sp Additionally, the username \fBpgbouncer\fP is allowed to log in without password, if the login comes via Unix socket and the client has same Unix user uid as the running process. .SS Show commands .sp The \fBSHOW\fP commands output information. Each command is described below. .SS SHOW STATS; .sp Shows statistics. .INDENT 0.0 .TP .B database Statistics are presented per database. .TP .B total_requests Total number of SQL requests pooled by \fBpgbouncer\fP. .TP .B total_received Total volume in bytes of network traffic received by \fBpgbouncer\fP. .TP .B total_sent Total volume in bytes of network traffic sent by \fBpgbouncer\fP. .TP .B total_query_time Total number of microseconds spent by \fBpgbouncer\fP when actively connected to PostgreSQL. .TP .B avg_req Average requests per second in last stat period. .TP .B avg_recv Average received (from clients) bytes per second. .TP .B avg_sent Average sent (to clients) bytes per second. .TP .B avg_query Average query duration in microseconds. .UNINDENT .SS SHOW SERVERS; .INDENT 0.0 .TP .B type S, for server. .TP .B user Username \fBpgbouncer\fP uses to connect to server. .TP .B database Database name. .TP .B state State of the pgbouncer server connection, one of \fBactive\fP, \fBused\fP or \fBidle\fP. .TP .B addr IP address of PostgreSQL server. .TP .B port Port of PostgreSQL server. .TP .B local_addr Connection start address on local machine. .TP .B local_port Connection start port on local machine. .TP .B connect_time When the connection was made. .TP .B request_time When last request was issued. .TP .B ptr Address of internal object for this connection. Used as unique ID. .TP .B link Address of client connection the server is paired with. .TP .B remote_pid Pid of backend server process. In case connection is made over unix socket and OS supports getting process ID info, it\(aqs OS pid. Otherwise it\(aqs extracted from cancel packet server sent, which should be PID in case server is Postgres, but it\(aqs a random number in case server it another PgBouncer. .UNINDENT .SS SHOW CLIENTS; .INDENT 0.0 .TP .B type C, for client. .TP .B user Client connected user. .TP .B database Database name. .TP .B state State of the client connection, one of \fBactive\fP, \fBused\fP, \fBwaiting\fP or \fBidle\fP. .TP .B addr IP address of client. .TP .B port Port client is connected to. .TP .B local_addr Connection end address on local machine. .TP .B local_port Connection end port on local machine. .TP .B connect_time Timestamp of connect time. .TP .B request_time Timestamp of latest client request. .TP .B ptr Address of internal object for this connection. Used as unique ID. .TP .B link Address of server connection the client is paired with. .TP .B remote_pid Process ID, in case client connects over UNIX socket and OS supports getting it. .UNINDENT .SS SHOW POOLS; .sp A new pool entry is made for each couple of (database, user). .INDENT 0.0 .TP .B database Database name. .TP .B user User name. .TP .B cl_active Client connections that are linked to server connection and can process queries. .TP .B cl_waiting Client connections have sent queries but have not yet got a server connection. .TP .B sv_active Server connections that linked to client. .TP .B sv_idle Server connections that unused and immediately usable for client queries. .TP .B sv_used Server connections that have been idle more than \fIserver_check_delay\fP, so they needs \fIserver_check_query\fP to run on it before it can be used. .TP .B sv_tested Server connections that are currently running either \fIserver_reset_query\fP or \fIserver_check_query\fP. .TP .B sv_login Server connections currently in logging in process. .TP .B maxwait How long the first (oldest) client in queue has waited, in seconds. If this starts increasing, then the current pool of servers does not handle requests quick enough. Reason may be either overloaded server or just too small of a \fBpool_size\fP setting. .TP .B pool_mode The pooling mode in use. .UNINDENT .SS SHOW LISTS; .sp Show following internal information, in columns (not rows): .INDENT 0.0 .TP .B databases Count of databases. .TP .B users Count of users. .TP .B pools Count of pools. .TP .B free_clients Count of free clients. .TP .B used_clients Count of used clients. .TP .B login_clients Count of clients in \fBlogin\fP state. .TP .B free_servers Count of free servers. .TP .B used_servers Count of used servers. .UNINDENT .SS SHOW USERS; .INDENT 0.0 .TP .B name The user name .TP .B pool_mode The user\(aqs override pool_mode, or NULL if the default will be used instead. .UNINDENT .SS SHOW DATABASES; .INDENT 0.0 .TP .B name Name of configured database entry. .TP .B host Host pgbouncer connects to. .TP .B port Port pgbouncer connects to. .TP .B database Actual database name pgbouncer connects to. .TP .B force_user When user is part of the connection string, the connection between pgbouncer and PostgreSQL is forced to the given user, whatever the client user. .TP .B pool_size Maximum number of server connections. .TP .B pool_mode The database\(aqs override pool_mode, or NULL if the default will be used instead. .UNINDENT .SS SHOW FDS; .sp Internal command \- shows list of fds in use with internal state attached to them. .sp When the connected user has username "pgbouncer", connects through Unix socket and has same UID as running process, the actual fds are passed over the connection. This mechanism is used to do an online restart. Note: This does not work on Windows machines. .sp This command also blocks internal event loop, so it should not be used while PgBouncer is in use. .INDENT 0.0 .TP .B fd File descriptor numeric value. .TP .B task One of \fBpooler\fP, \fBclient\fP or \fBserver\fP. .TP .B user User of the connection using the FD. .TP .B database Database of the connection using the FD. .TP .B addr IP address of the connection using the FD, \fBunix\fP if a unix socket is used. .TP .B port Port used by the connection using the FD. .TP .B cancel Cancel key for this connection. .TP .B link fd for corresponding server/client. NULL if idle. .UNINDENT .SS SHOW CONFIG; .sp Show the current configuration settings, one per row, with following columns: .INDENT 0.0 .TP .B key Configuration variable name .TP .B value Configuration value .TP .B changeable Either \fByes\fP or \fBno\fP, shows if the variable can be changed while running. If \fBno\fP, the variable can be changed only boot\-time. .UNINDENT .SS SHOW DNS_HOSTS; .sp Show hostnames in DNS cache. .INDENT 0.0 .TP .B hostname Host name. .TP .B ttl How meny seconds until next lookup. .TP .B addrs Comma separated list of addresses. .UNINDENT .SS SHOW DNS_ZONES .sp Show DNS zones in cache. .INDENT 0.0 .TP .B zonename Zone name. .TP .B serial Current serial. .TP .B count Hostnames belonging to this zone. .UNINDENT .SS Process controlling commands .SS PAUSE [db]; .sp PgBouncer tries to disconnect from all servers, first waiting for all queries to complete. The command will not return before all queries are finished. To be used at the time of database restart. .sp If database name is given, only that database will be paused. .SS DISABLE db; .sp Reject all new client connections on the given database. .SS ENABLE db; .sp Allow new client connections after a previous \fBDISABLE\fP command. .SS KILL db; .sp Immediately drop all client and server connections on given database. .SS SUSPEND; .sp All socket buffers are flushed and PgBouncer stops listening for data on them. The command will not return before all buffers are empty. To be used at the time of PgBouncer online reboot. .SS RESUME [db]; .sp Resume work from previous \fBPAUSE\fP or \fBSUSPEND\fP command. .SS SHUTDOWN; .sp The PgBouncer process will exit. .SS RELOAD; .sp The PgBouncer process will reload its configuration file and update changeable settings. .SS Signals .INDENT 0.0 .TP .B SIGHUP Reload config. Same as issuing command \fBRELOAD;\fP on console. .TP .B SIGINT Safe shutdown. Same as issuing \fBPAUSE;\fP and \fBSHUTDOWN;\fP on console. .TP .B SIGTERM Immediate shutdown. Same as issuing \fBSHUTDOWN;\fP on console. .UNINDENT .SS Libevent settings .sp From libevent docs: .sp .nf .ft C It is possible to disable support for epoll, kqueue, devpoll, poll or select by setting the environment variable EVENT_NOEPOLL, EVENT_NOKQUEUE, EVENT_NODEVPOLL, EVENT_NOPOLL or EVENT_NOSELECT, respectively. By setting the environment variable EVENT_SHOW_METHOD, libevent displays the kernel notification method that it uses. .ft P .fi .SH SEE ALSO .sp pgbouncer(5) \- manpage of configuration settings descriptions. .sp \fI\%https://pgbouncer.github.io/\fP .sp \fI\%https://wiki.postgresql.org/wiki/PgBouncer\fP .\" Generated by docutils manpage writer. .\" . pgbouncer-1.7/doc/Makefile0000664000175000017500000000114212572127367012507 00000000000000-include ../config.mak RST2MAN ?= rst2man manpages = pgbouncer.1 pgbouncer.5 dist_man_MANS = $(manpages) EXTRA_DIST = config.rst usage.rst Makefile $(manpages) \ frag-config-man frag-usage-man # make maintainer-clean removes those MAINTAINERCLEANFILES = $(manpages) SUBLOC = doc abs_top_srcdir ?= $(CURDIR)/.. include $(abs_top_srcdir)/lib/mk/antimake.mk pgbouncer.1: usage.rst frag-usage-man $(SED) -e '4r frag-usage-man' usage.rst | $(RST2MAN) > $@ pgbouncer.5: config.rst frag-config-man $(SED) -e '4r frag-config-man' config.rst | $(RST2MAN) > $@ web: make -C ../../pgbouncer.github.io pgbouncer-1.7/debian/0000775000175000017500000000000012635051616011577 500000000000000pgbouncer-1.7/debian/copyright0000664000175000017500000000136412060213747013453 00000000000000 Copyright (C) 2007-2011 Marko Kreen, Skype Technologies Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. pgbouncer-1.7/debian/rules0000775000175000017500000000035312060213747012575 00000000000000#! /usr/bin/make -f DH_VERBOSE = 1 DEB_BUILD_OPTIONS = nostrip export DEB_BUILD_OPTIONS override DEB_CONFIGURE_EXTRA_FLAGS = --enable-debug include /usr/share/cdbs/1/rules/debhelper.mk include /usr/share/cdbs/1/class/autotools.mk pgbouncer-1.7/debian/changelog0000664000175000017500000001546112635044402013373 00000000000000pgbouncer (1.7-1) unstable; urgency=low * v1.7 -- Marko Kreen Fri, 18 Dec 2015 19:57:16 +0200 pgbouncer (1.7~rc1-1) unstable; urgency=low * 1.7rc1 -- Marko Kreen Mon, 02 Nov 2015 18:11:58 +0200 pgbouncer (1.7~dev-1) unstable; urgency=low * v1.7dev -- Marko Kreen Mon, 03 Aug 2015 23:47:46 +0300 pgbouncer (1.6-1) unstable; urgency=low * v1.6 -- Petr Jelinek Sat, 01 Aug 2015 11:53:28 +0200 pgbouncer (1.6rc1-1) unstable; urgency=low * v1.6rc1 -- Marko Kreen Tue, 30 Dec 2014 16:19:51 +0200 pgbouncer (1.6~dev-1) unstable; urgency=low * v1.6dev -- Marko Kreen Tue, 08 Oct 2013 00:37:48 +0300 pgbouncer (1.5.4-1) unstable; urgency=low * v1.5.4 -- Marko Kreen Wed, 28 Nov 2012 12:44:39 +0200 pgbouncer (1.5.3-1) unstable; urgency=low * v1.5.3 -- Marko Kreen Wed, 12 Sep 2012 13:36:51 +0300 pgbouncer (1.5.2-1) unstable; urgency=low * v1.5.2 -- Marko Kreen Tue, 29 May 2012 11:02:48 +0300 pgbouncer (1.5.1-1) unstable; urgency=low * v1.5.1 -- Marko Kreen Tue, 17 Apr 2012 16:04:13 +0300 pgbouncer (1.5.1rc1-1) unstable; urgency=low * v1.5.1rc1 -- Marko Kreen Wed, 14 Mar 2012 14:31:54 +0200 pgbouncer (1.5-1) unstable; urgency=low * v1.5 -- Marko Kreen Thu, 05 Jan 2012 14:59:43 +0200 pgbouncer (1.5rc1-1) unstable; urgency=low * v1.5rc1 -- Marko Kreen Tue, 13 Dec 2011 00:28:43 +0200 pgbouncer (1.4.3dev1-1) unstable; urgency=low * v1.4.3dev1 -- Marko Kreen Thu, 01 Sep 2011 11:49:12 +0300 pgbouncer (1.4.2rc1-1) unstable; urgency=low * v1.4.2rc1 -- Marko Kreen Thu, 02 Jun 2011 18:36:27 +0300 pgbouncer (1.4.1-1) unstable; urgency=low * v1.4.1 -- Marko Kreen Fri, 01 Apr 2011 21:51:54 +0300 pgbouncer (1.4.1rc5-1) unstable; urgency=low * v1.4.1rc5 -- Marko Kreen Tue, 29 Mar 2011 17:00:55 +0300 pgbouncer (1.4.1rc3-1) unstable; urgency=low * v1.4.1rc4 -- Marko Kreen Sat, 26 Mar 2011 00:40:50 +0200 pgbouncer (1.4.1rc3-1) unstable; urgency=low * v1.4.1rc3 -- Marko Kreen Wed, 23 Mar 2011 18:06:01 +0200 pgbouncer (1.4.1rc2-1) unstable; urgency=low * v1.4.1rc2 -- Marko Kreen Mon, 21 Mar 2011 23:24:00 +0200 pgbouncer (1.4.1rc1-1) unstable; urgency=low * v1.4.1rc1 -- Marko Kreen Mon, 28 Feb 2011 23:11:27 +0200 pgbouncer (1.4-1) unstable; urgency=low * v1.4 -- Marko Kreen Wed, 08 Dec 2010 17:30:06 +0200 pgbouncer (1.4-0rc4) unstable; urgency=low * v1.4rc4 -- Marko Kreen Tue, 30 Nov 2010 11:54:55 +0200 pgbouncer (1.4-0rc3) unstable; urgency=low * v1.4rc3 -- Marko Kreen Mon, 29 Nov 2010 13:44:46 +0200 pgbouncer (1.4-0rc2.1) unstable; urgency=low * v1.4rc2.1 -- Marko Kreen Thu, 18 Nov 2010 10:26:25 +0200 pgbouncer (1.4-0rc2) unstable; urgency=low * v1.4rc2 -- Marko Kreen Wed, 17 Nov 2010 15:11:13 +0200 pgbouncer (1.4-0rc1.1) unstable; urgency=low * v1.4rc1.1 -- Marko Kreen Wed, 17 Nov 2010 14:10:25 +0200 pgbouncer (1.4-0rc1) unstable; urgency=low * v1.4rc1 -- Marko Kreen Mon, 08 Nov 2010 11:02:57 +0200 pgbouncer (1.4-0rc0) unstable; urgency=low * v1.4rc0 -- Marko Kreen Wed, 03 Nov 2010 15:14:10 +0200 pgbouncer (1.4-0alpha1) unstable; urgency=low * v1.4 alpha 1 -- Marko Kreen Mon, 20 Sep 2010 13:37:15 -0700 pgbouncer (1.3.3-0rc1) unstable; urgency=low * v1.3.3rc1 -- Marko Kreen Fri, 23 Apr 2010 17:33:07 +0300 pgbouncer (1.3.2-1) unstable; urgency=low * v1.3.2 -- Marko Kreen Mon, 15 Mar 2010 16:09:16 +0200 pgbouncer (1.3.2rc1-1) unstable; urgency=low * v1.3.2rc1 -- Marko Kreen Fri, 04 Dec 2009 13:18:26 +0200 pgbouncer (1.3.1-1) unstable; urgency=low * v1.3.1 -- Marko Kreen Mon, 06 Jul 2009 16:12:53 +0300 pgbouncer (1.3.1rc1-1) unstable; urgency=low * v1.3.1rc1 -- Marko Kreen Fri, 26 Jun 2009 14:25:42 +0300 pgbouncer (1.3-1) unstable; urgency=low * v1.3 -- Marko Kreen Wed, 18 Feb 2009 14:11:41 +0200 pgbouncer (1.3rc1-0) unstable; urgency=low * v1.3rc1 -- Marko Kreen Fri, 16 jan 2009 15:28:49 +0200 pgbouncer (1.2.3-1) unstable; urgency=low * v.1.2.3 -- Marko Kreen Fri, 08 Aug 2008 14:35:27 +0300 pgbouncer (1.2.2-1) unstable; urgency=low * v1.2.2 -- Marko Kreen Wed, 06 Aug 2008 09:44:15 +0300 pgbouncer (1.2.1-1) unstable; urgency=low * v1.2.1 -- Marko Kreen Fri, 01 Aug 2008 13:00:30 +0300 pgbouncer (1.2-1) unstable; urgency=low * v1.2 -- Marko Kreen Tue, 29 Jul 2008 14:18:59 +0300 pgbouncer (1.2-0rc1) unstable; urgency=low * v1.2rc1 -- Marko Kreen Wed, 25 Jun 2008 21:02:08 +0300 pgbouncer (1.1.1-1) unstable; urgency=low * PgBouncer 1.1.1 -- Marko Kreen Mon, 15 Oct 2007 17:41:05 +0300 pgbouncer (1.0.8-1) unstable; urgency=low * Fix crash with ^C from psql. * PAUSE db; RESUME db; -- Marko Kreen Mon, 18 Jun 2007 15:23:32 +0300 pgbouncer (1.0.7-1) unstable; urgency=low * Fix send() blocking on flush packets. -- Marko Kreen Wed, 18 Apr 2007 16:43:45 +0300 pgbouncer (1.0.6-1) unstable; urgency=low * previous fix could broke maint. -- Marko Kreen Thu, 12 Apr 2007 11:30:53 +0300 pgbouncer (1.0.5-1) unstable; urgency=low * fix online restart bugs. -- Marko Kreen Wed, 11 Apr 2007 13:50:50 +0300 pgbouncer (1.0.4-1) unstable; urgency=low * last bug, honestly. -- Marko Kreen Wed, 11 Apr 2007 12:03:52 +0300 pgbouncer (1.0.3-1) unstable; urgency=low * more error handling fixes. -- Marko Kreen Tue, 10 Apr 2007 17:22:49 +0300 pgbouncer (1.0.2-1) unstable; urgency=low * 2 more bugs. -- Marko Kreen Wed, 28 Mar 2007 12:04:39 +0300 pgbouncer (1.0.1) unstable; urgency=low * Couple hotfixes. -- Marko Kreen Wed, 14 Mar 2007 21:30:44 +0200 pgbouncer (1.0) unstable; urgency=low * Public release. -- Marko Kreen Tue, 13 Mar 2007 17:30:02 +0200 pgbouncer-1.7/debian/compat0000664000175000017500000000000212060213747012712 000000000000005 pgbouncer-1.7/debian/control0000664000175000017500000000055312133556723013127 00000000000000Source: pgbouncer Section: database Priority: extra Maintainer: Marko Kreen Standards-Version: 3.6.2 Build-Depends: cdbs, debhelper (>= 7), libevent-dev (>= 1.3b), asciidoc, xmlto, make (>= 3.81) Package: pgbouncer Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: Lightweight connection pooler for PostgreSQL . pgbouncer-1.7/include/0000775000175000017500000000000012635051616012000 500000000000000pgbouncer-1.7/include/loader.h0000664000175000017500000000221612511203334013325 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* connstring parsing */ bool parse_database(void *base, const char *name, const char *connstr); bool parse_user(void *base, const char *name, const char *params); /* user file parsing */ bool load_auth_file(const char *fn) /* _MUSTCHECK */; bool loader_users_check(void) /* _MUSTCHECK */; pgbouncer-1.7/include/takeover.h0000664000175000017500000000176312060213747013715 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ void takeover_init(void); bool takeover_login(PgSocket *bouncer) _MUSTCHECK; void takeover_login_failed(void); void takeover_finish(void); pgbouncer-1.7/include/bouncer.h0000664000175000017500000003565712572127367013555 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * core structures */ #include "system.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DBGVER #define FULLVER PACKAGE_NAME " version " PACKAGE_VERSION " (" DBGVER ")" #else #define FULLVER PACKAGE_NAME " version " PACKAGE_VERSION #endif /* each state corresponds to a list */ enum SocketState { CL_FREE, /* free_client_list */ CL_JUSTFREE, /* justfree_client_list */ CL_LOGIN, /* login_client_list */ CL_WAITING, /* pool->waiting_client_list */ CL_WAITING_LOGIN, /* - but return to CL_LOGIN instead of CL_ACTIVE */ CL_ACTIVE, /* pool->active_client_list */ CL_CANCEL, /* pool->cancel_req_list */ SV_FREE, /* free_server_list */ SV_JUSTFREE, /* justfree_server_list */ SV_LOGIN, /* pool->new_server_list */ SV_IDLE, /* pool->idle_server_list */ SV_ACTIVE, /* pool->active_server_list */ SV_USED, /* pool->used_server_list */ SV_TESTED /* pool->tested_server_list */ }; enum PauseMode { P_NONE = 0, /* active pooling */ P_PAUSE = 1, /* wait for client to finish work */ P_SUSPEND = 2 /* wait for buffers to be empty */ }; enum SSLMode { SSLMODE_DISABLED, SSLMODE_ALLOW, SSLMODE_PREFER, SSLMODE_REQUIRE, SSLMODE_VERIFY_CA, SSLMODE_VERIFY_FULL }; #define is_server_socket(sk) ((sk)->state >= SV_FREE) typedef struct PgSocket PgSocket; typedef struct PgUser PgUser; typedef struct PgDatabase PgDatabase; typedef struct PgPool PgPool; typedef struct PgStats PgStats; typedef union PgAddr PgAddr; typedef enum SocketState SocketState; typedef struct PktHdr PktHdr; extern int cf_sbuf_len; #include "util.h" #include "iobuf.h" #include "sbuf.h" #include "pktbuf.h" #include "varcache.h" #include "dnslookup.h" #include "admin.h" #include "loader.h" #include "client.h" #include "server.h" #include "pooler.h" #include "proto.h" #include "objects.h" #include "stats.h" #include "takeover.h" #include "janitor.h" #include "hba.h" /* to avoid allocations will use static buffers */ #define MAX_DBNAME 64 #define MAX_USERNAME 64 #define MAX_PASSWORD 64 /* no-auth modes */ #define AUTH_ANY -1 /* same as trust but without username check */ #define AUTH_TRUST AUTH_OK /* protocol codes */ #define AUTH_OK 0 #define AUTH_KRB 2 #define AUTH_PLAIN 3 #define AUTH_CRYPT 4 #define AUTH_MD5 5 #define AUTH_CREDS 6 /* internal codes */ #define AUTH_CERT 7 #define AUTH_PEER 8 #define AUTH_HBA 9 #define AUTH_REJECT 10 /* type codes for weird pkts */ #define PKT_STARTUP_V2 0x20000 #define PKT_STARTUP 0x30000 #define PKT_CANCEL 80877102 #define PKT_SSLREQ 80877103 #define POOL_SESSION 0 #define POOL_TX 1 #define POOL_STMT 2 #define POOL_INHERIT 3 #define BACKENDKEY_LEN 8 /* buffer size for startup noise */ #define STARTUP_BUF 1024 /* * Remote/local address */ /* buffer for pgaddr string conversions (with port) */ #define PGADDR_BUF (INET6_ADDRSTRLEN + 10) struct sockaddr_ucreds { struct sockaddr_in sin; uid_t uid; pid_t pid; }; /* * AF_INET,AF_INET6 are stored as-is, * AF_UNIX uses sockaddr_in port + uid/pid. */ union PgAddr { struct sockaddr sa; struct sockaddr_in sin; struct sockaddr_in6 sin6; struct sockaddr_ucreds scred; }; static inline unsigned int pga_family(const PgAddr *a) { return a->sa.sa_family; } static inline bool pga_is_unix(const PgAddr *a) { return a->sa.sa_family == AF_UNIX; } int pga_port(const PgAddr *a); void pga_set(PgAddr *a, int fam, int port); void pga_copy(PgAddr *a, const struct sockaddr *sa); bool pga_pton(PgAddr *a, const char *s, int port); const char *pga_ntop(const PgAddr *a, char *dst, int dstlen); const char *pga_str(const PgAddr *a, char *dst, int dstlen); const char *pga_details(const PgAddr *a, char *dst, int dstlen); int pga_cmp_addr(const PgAddr *a, const PgAddr *b); /* * Stats, kept per-pool. */ struct PgStats { uint64_t request_count; uint64_t server_bytes; uint64_t client_bytes; usec_t query_time; /* total req time in us */ }; /* * Contains connections for one db+user pair. * * Stats: * ->stats is updated online. * for each stats_period: * ->older_stats = ->newer_stats * ->newer_stats = ->stats */ struct PgPool { struct List head; /* entry in global pool_list */ struct List map_head; /* entry in user->pool_list */ PgDatabase *db; /* corresponding database */ PgUser *user; /* user logged in as */ struct StatList active_client_list; /* waiting events logged in clients */ struct StatList waiting_client_list; /* client waits for a server to be available */ struct StatList cancel_req_list; /* closed client connections with server key */ struct StatList active_server_list; /* servers linked with clients */ struct StatList idle_server_list; /* servers ready to be linked with clients */ struct StatList used_server_list; /* server just unlinked from clients */ struct StatList tested_server_list; /* server in testing process */ struct StatList new_server_list; /* servers in login phase */ PgStats stats; PgStats newer_stats; PgStats older_stats; /* database info to be sent to client */ struct PktBuf *welcome_msg; /* ServerParams without VarCache ones */ VarCache orig_vars; /* default params from server */ usec_t last_lifetime_disconnect;/* last time when server_lifetime was applied */ /* if last connect failed, there should be delay before next */ usec_t last_connect_time; unsigned last_connect_failed:1; unsigned welcome_msg_ready:1; }; #define pool_connected_server_count(pool) ( \ statlist_count(&(pool)->active_server_list) + \ statlist_count(&(pool)->idle_server_list) + \ statlist_count(&(pool)->tested_server_list) + \ statlist_count(&(pool)->used_server_list)) #define pool_server_count(pool) ( \ pool_connected_server_count(pool) + \ statlist_count(&(pool)->new_server_list)) #define pool_client_count(pool) ( \ statlist_count(&(pool)->active_client_list) + \ statlist_count(&(pool)->waiting_client_list)) /* * A user in login db. * * FIXME: remove ->head as ->tree_node should be enough. * * For databases where remote user is forced, the pool is: * first(db->forced_user->pool_list), where pool_list has only one entry. * * Otherwise, ->pool_list contains multiple pools, for all PgDatabases * which user has logged in. */ struct PgUser { struct List head; /* used to attach user to list */ struct List pool_list; /* list of pools where pool->user == this user */ struct AANode tree_node; /* used to attach user to tree */ char name[MAX_USERNAME]; char passwd[MAX_PASSWORD]; int pool_mode; int max_user_connections; /* how much server connections are allowed */ int connection_count; /* how much connections are used by user now */ }; /* * A database entry from config. */ struct PgDatabase { struct List head; char name[MAX_DBNAME]; /* db name for clients */ bool db_paused; /* PAUSE ; was issued */ bool db_dead; /* used on RELOAD/SIGHUP to later detect removed dbs */ bool db_auto; /* is the database auto-created by autodb_connstr */ bool db_disabled; /* is the database accepting new connections? */ bool admin; /* internal console db */ struct PktBuf *startup_params; /* partial StartupMessage (without user) be sent to server */ PgUser *forced_user; /* if not NULL, the user/psw is forced */ PgUser *auth_user; /* if not NULL, users not in userlist.txt will be looked up on the server */ const char *host; /* host or unix socket name */ int port; int pool_size; /* max server connections in one pool */ int res_pool_size; /* additional server connections in case of trouble */ int pool_mode; /* pool mode for this database */ int max_db_connections; /* max server connections between all pools */ const char *dbname; /* server-side name, pointer to inside startup_msg */ /* startup commands to send to server after connect. malloc-ed */ const char *connect_query; usec_t inactive_time; /* when auto-database became inactive (to kill it after timeout) */ unsigned active_stamp; /* set if autodb has connections */ int connection_count; /* total connections for this database in all pools */ struct AATree user_tree; /* users that have been queried on this database */ }; /* * A client or server connection. * * ->state corresponds to various lists the struct can be at. */ struct PgSocket { struct List head; /* list header */ PgSocket *link; /* the dest of packets */ PgPool *pool; /* parent pool, if NULL not yet assigned */ PgUser *auth_user; /* presented login, for client it may differ from pool->user */ int client_auth_type; /* auth method decided by hba */ SocketState state:8; /* this also specifies socket location */ bool ready:1; /* server: accepts new query */ bool idle_tx:1; /* server: idling in tx */ bool close_needed:1; /* server: this socket must be closed ASAP */ bool setting_vars:1; /* server: setting client vars */ bool exec_on_connect:1; /* server: executing connect_query */ bool resetting:1; /* server: executing reset query from auth login; don't release on flush */ bool copy_mode:1; /* server: in copy stream, ignores any Sync packets */ bool wait_for_welcome:1;/* client: no server yet in pool, cannot send welcome msg */ bool wait_for_user_conn:1;/* client: waiting for auth_conn server connection */ bool wait_for_user:1; /* client: waiting for auth_conn query results */ bool suspended:1; /* client/server: if the socket is suspended */ bool admin_user:1; /* console client: has admin rights */ bool own_user:1; /* console client: client with same uid on unix socket */ bool wait_for_response:1;/* console client: waits for completion of PAUSE/SUSPEND cmd */ bool wait_sslchar:1; /* server: waiting for ssl response: S/N */ int expect_rfq_count; /* client: count of ReadyForQuery packets client should see */ usec_t connect_time; /* when connection was made */ usec_t request_time; /* last activity time */ usec_t query_start; /* query start moment */ uint8_t cancel_key[BACKENDKEY_LEN]; /* client: generated, server: remote */ PgAddr remote_addr; /* ip:port for remote endpoint */ PgAddr local_addr; /* ip:port for local endpoint */ union { struct DNSToken *dns_token; /* ongoing request */ PgDatabase *db; /* cache db while doing auth query */ }; VarCache vars; /* state of interesting server parameters */ SBuf sbuf; /* stream buffer, must be last */ }; #define RAW_IOBUF_SIZE offsetof(IOBuf, buf) #define IOBUF_SIZE (RAW_IOBUF_SIZE + cf_sbuf_len) /* where to store old fd info during SHOW FDS result processing */ #define tmp_sk_oldfd request_time #define tmp_sk_linkfd query_start /* takeover_clean_socket() needs to clean those up */ /* where the salt is temporarily stored */ #define tmp_login_salt cancel_key /* main.c */ extern int cf_daemon; extern char *cf_config_file; extern char *cf_jobname; extern char *cf_unix_socket_dir; extern int cf_unix_socket_mode; extern char *cf_unix_socket_group; extern char *cf_listen_addr; extern int cf_listen_port; extern int cf_listen_backlog; extern int cf_pool_mode; extern int cf_max_client_conn; extern int cf_default_pool_size; extern int cf_min_pool_size; extern int cf_res_pool_size; extern usec_t cf_res_pool_timeout; extern int cf_max_db_connections; extern int cf_max_user_connections; extern char * cf_autodb_connstr; extern usec_t cf_autodb_idle_timeout; extern usec_t cf_suspend_timeout; extern usec_t cf_server_lifetime; extern usec_t cf_server_idle_timeout; extern char * cf_server_reset_query; extern int cf_server_reset_query_always; extern char * cf_server_check_query; extern usec_t cf_server_check_delay; extern usec_t cf_server_connect_timeout; extern usec_t cf_server_login_retry; extern usec_t cf_query_timeout; extern usec_t cf_query_wait_timeout; extern usec_t cf_client_idle_timeout; extern usec_t cf_client_login_timeout; extern usec_t cf_idle_transaction_timeout; extern int cf_server_round_robin; extern int cf_disable_pqexec; extern usec_t cf_dns_max_ttl; extern usec_t cf_dns_nxdomain_ttl; extern usec_t cf_dns_zone_check_period; extern int cf_auth_type; extern char *cf_auth_file; extern char *cf_auth_query; extern char *cf_auth_hba_file; extern char *cf_pidfile; extern char *cf_ignore_startup_params; extern char *cf_admin_users; extern char *cf_stats_users; extern int cf_stats_period; extern int cf_pause_mode; extern int cf_shutdown; extern int cf_reboot; extern unsigned int cf_max_packet_size; extern int cf_sbuf_loopcnt; extern int cf_tcp_keepalive; extern int cf_tcp_keepcnt; extern int cf_tcp_keepidle; extern int cf_tcp_keepintvl; extern int cf_tcp_socket_buffer; extern int cf_tcp_defer_accept; extern int cf_log_connections; extern int cf_log_disconnections; extern int cf_log_pooler_errors; extern int cf_application_name_add_host; extern int cf_client_tls_sslmode; extern char *cf_client_tls_protocols; extern char *cf_client_tls_ca_file; extern char *cf_client_tls_cert_file; extern char *cf_client_tls_key_file; extern char *cf_client_tls_ciphers; extern char *cf_client_tls_dheparams; extern char *cf_client_tls_ecdhecurve; extern int cf_server_tls_sslmode; extern char *cf_server_tls_protocols; extern char *cf_server_tls_ca_file; extern char *cf_server_tls_cert_file; extern char *cf_server_tls_key_file; extern char *cf_server_tls_ciphers; extern const struct CfLookup pool_mode_map[]; extern usec_t g_suspend_start; extern struct DNSContext *adns; extern struct HBA *parsed_hba; static inline PgSocket * _MUSTCHECK pop_socket(struct StatList *slist) { struct List *item = statlist_pop(slist); if (item == NULL) return NULL; return container_of(item, PgSocket, head); } static inline PgSocket * first_socket(struct StatList *slist) { if (statlist_empty(slist)) return NULL; return container_of(slist->head.next, PgSocket, head); } static inline PgSocket * last_socket(struct StatList *slist) { if (statlist_empty(slist)) return NULL; return container_of(slist->head.prev, PgSocket, head); } void load_config(void); bool set_config_param(const char *key, const char *val); void config_for_each(void (*param_cb)(void *arg, const char *name, const char *val, bool reloadable), void *arg); pgbouncer-1.7/include/util.h0000664000175000017500000000452112556777177013073 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * logging about specific socket */ int log_socket_prefix(enum LogLevel lev, void *ctx, char *dst, unsigned int dstlen); #define slog_error(sk, args...) log_generic(LG_ERROR, sk, ## args) #define slog_warning(sk, args...) log_generic(LG_WARNING, sk, ## args) #define slog_info(sk, args...) log_generic(LG_INFO, sk, ## args) #define slog_debug(sk, args...) do { \ if (unlikely(cf_verbose > 0)) \ log_generic(LG_DEBUG, sk, ## args); \ } while (0) #define slog_noise(sk, args...) do { \ if (unlikely(cf_verbose > 1)) \ log_generic(LG_NOISE, sk, ## args); \ } while (0) /* * password tools */ #define MD5_PASSWD_LEN 35 #define isMD5(passwd) (memcmp(passwd, "md5", 3) == 0 \ && strlen(passwd) == MD5_PASSWD_LEN) void pg_md5_encrypt(const char *part1, const char *part2, size_t p2len, char *dest); void get_random_bytes(uint8_t *dest, int len); const char *bin2hex(const uint8_t *src, unsigned srclen, char *dst, unsigned dstlen); bool tune_socket(int sock, bool is_unix) _MUSTCHECK; bool strlist_contains(const char *liststr, const char *str); void fill_remote_addr(PgSocket *sk, int fd, bool is_unix); void fill_local_addr(PgSocket *sk, int fd, bool is_unix); void rescue_timers(void); void safe_evtimer_add(struct event *ev, struct timeval *tv); /* log truncated strings */ #define safe_strcpy(dst, src, dstlen) do { \ size_t needed = strlcpy(dst, src, dstlen); \ if (unlikely(needed >= (dstlen))) \ log_warning("bug in %s:%d - string truncated", __FILE__, __LINE__); \ } while (0) pgbouncer-1.7/include/pooler.h0000664000175000017500000000225012060213747013365 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ void pooler_setup(void); bool use_pooler_socket(int fd, bool is_unix) _MUSTCHECK; void resume_pooler(void); void suspend_pooler(void); void per_loop_pooler_maint(void); void pooler_tune_accept(bool on); typedef bool (*pooler_cb)(void *arg, int fd, const PgAddr *addr); bool for_each_pooler_fd(pooler_cb cb, void *arg); pgbouncer-1.7/include/stats.h0000664000175000017500000000204712060213747013227 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ void stats_setup(void); bool admin_database_stats(PgSocket *client, struct StatList *pool_list) _MUSTCHECK; bool show_stat_totals(PgSocket *client, struct StatList *pool_list) _MUSTCHECK; pgbouncer-1.7/include/hba.h0000664000175000017500000000176212572127367012640 00000000000000/* * Host-Based-Access-control file support. * * Copyright (c) 2015 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ struct HBA; struct HBA *hba_load_rules(const char *fn); void hba_free(struct HBA *hba); int hba_eval(struct HBA *hba, PgAddr *addr, bool is_tls, const char *dbname, const char *username); pgbouncer-1.7/include/system.h0000664000175000017500000000331412576104575013425 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Required system headers */ #include #include #ifdef WIN32 #include "win32support.h" #endif #include #include #include #include #ifdef HAVE_LIBGEN_H #include #endif #ifdef HAVE_SYS_UIO_H #include #endif #ifndef UNIX_PATH_MAX #define UNIX_PATH_MAX 128 /* actual sizeof() will be applied later anyway */ #endif /* * PostgreSQL type OIDs for resultsets. */ #define INT8OID 20 #define INT4OID 23 #define TEXTOID 25 /* * libc compat functions. */ #ifndef HAVE_LSTAT static inline int lstat(const char *path, struct stat *st) { return stat(path, st); } #endif bool check_unix_peer_name(int fd, const char *username); void change_user(const char *user); void change_file_mode(const char *fn, mode_t mode, const char *user, const char *group); pgbouncer-1.7/include/server.h0000664000175000017500000000211712511203334013365 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ bool server_proto(SBuf *sbuf, SBufEvent evtype, struct MBuf *pkt) _MUSTCHECK; int pool_pool_mode(PgPool *pool) _MUSTCHECK; int database_max_connections(PgDatabase *db) _MUSTCHECK; int user_max_connections(PgUser *user) _MUSTCHECK; pgbouncer-1.7/include/varcache.h0000664000175000017500000000100312060213747013634 00000000000000 enum VarCacheIdx { VDateStyle = 0, VClientEncoding, VTimeZone, VStdStr, VAppName, NumVars }; typedef struct VarCache VarCache; struct VarCache { struct PStr *var_list[NumVars]; }; bool varcache_set(VarCache *cache, const char *key, const char *value) /* _MUSTCHECK */; bool varcache_apply(PgSocket *server, PgSocket *client, bool *changes_p) _MUSTCHECK; void varcache_fill_unset(VarCache *src, PgSocket *dst); void varcache_clean(VarCache *cache); void varcache_add_params(PktBuf *pkt, VarCache *vars); pgbouncer-1.7/include/admin.h0000664000175000017500000000257312511203334013155 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ bool admin_handle_client(PgSocket *client, PktHdr *pkt) _MUSTCHECK; bool admin_pre_login(PgSocket *client, const char *username) _MUSTCHECK; bool admin_post_login(PgSocket *client) _MUSTCHECK; void admin_setup(void); bool admin_error(PgSocket *console, const char *fmt, ...) _PRINTF(2, 3) /* _MUSTCHECK */; void admin_pause_done(void); bool admin_flush(PgSocket *admin, PktBuf *buf, const char *desc) /* _MUSTCHECK */; bool admin_ready(PgSocket *admin, const char *desc) _MUSTCHECK; void admin_handle_cancel(PgSocket *client); pgbouncer-1.7/include/pktbuf.h0000664000175000017500000001053412572127367013376 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Safe & easy creation of PostgreSQL packets. */ typedef struct PktBuf PktBuf; struct PktBuf { uint8_t *buf; int buf_len; int write_pos; int pktlen_pos; int send_pos; struct event *ev; PgSocket *queued_dst; unsigned failed:1; unsigned sending:1; unsigned fixed_buf:1; }; /* * pktbuf creation */ 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 */ bool pktbuf_send_immediate(PktBuf *buf, PgSocket *sk) _MUSTCHECK; bool pktbuf_send_queued(PktBuf *buf, PgSocket *sk) _MUSTCHECK; /* * low-level ops */ void pktbuf_start_packet(PktBuf *buf, int type); void pktbuf_put_char(PktBuf *buf, char val); void pktbuf_put_uint16(PktBuf *buf, uint16_t val); void pktbuf_put_uint32(PktBuf *buf, uint32_t val); void pktbuf_put_uint64(PktBuf *buf, uint64_t val); void pktbuf_put_string(PktBuf *buf, const char *str); void pktbuf_put_bytes(PktBuf *buf, const void *data, int len); void pktbuf_finish_packet(PktBuf *buf); #define pktbuf_written(buf) ((buf)->write_pos) /* * Packet writing */ void pktbuf_write_generic(PktBuf *buf, int type, const char *fmt, ...); void pktbuf_write_RowDescription(PktBuf *buf, const char *tupdesc, ...); void pktbuf_write_DataRow(PktBuf *buf, const char *tupdesc, ...); void pktbuf_write_ExtQuery(PktBuf *buf, const char *query, int nargs, ...); /* * Shortcuts for actual packets. */ #define pktbuf_write_ParameterStatus(buf, key, val) \ pktbuf_write_generic(buf, 'S', "ss", key, val) #define pktbuf_write_AuthenticationOk(buf) \ pktbuf_write_generic(buf, 'R', "i", 0) #define pktbuf_write_ReadyForQuery(buf) \ pktbuf_write_generic(buf, 'Z', "c", 'I') #define pktbuf_write_CommandComplete(buf, desc) \ pktbuf_write_generic(buf, 'C', "s", desc) #define pktbuf_write_BackendKeyData(buf, key) \ pktbuf_write_generic(buf, 'K', "b", key, 8) #define pktbuf_write_CancelRequest(buf, key) \ pktbuf_write_generic(buf, PKT_CANCEL, "b", key, 8) #define pktbuf_write_StartupMessage(buf, user, parms, parms_len) \ pktbuf_write_generic(buf, PKT_STARTUP, "bsss", parms, parms_len, "user", user, "") #define pktbuf_write_PasswordMessage(buf, psw) \ pktbuf_write_generic(buf, 'p', "s", psw) #define pktbuf_write_Notice(buf, msg) \ pktbuf_write_generic(buf, 'N', "sscss", "SNOTICE", "C00000", 'M', msg, ""); #define pktbuf_write_SSLRequest(buf) \ pktbuf_write_generic(buf, PKT_SSLREQ, "") /* * Shortcut for creating DataRow in memory. */ #define BUILD_DataRow(reslen, dst, dstlen, args...) do { \ PktBuf _buf; \ pktbuf_static(&_buf, dst, dstlen); \ pktbuf_write_DataRow(&_buf, ## args); \ reslen = _buf.failed ? -1 : _buf.write_pos; \ } while (0) /* * Shortcuts for immediate send of one packet. */ #define SEND_wrap(buflen, pktfn, res, sk, args...) do { \ uint8_t _data[buflen]; PktBuf _buf; \ pktbuf_static(&_buf, _data, sizeof(_data)); \ pktfn(&_buf, ## args); \ res = pktbuf_send_immediate(&_buf, sk); \ } while (0) #define SEND_RowDescription(res, sk, args...) \ SEND_wrap(512, pktbuf_write_RowDescription, res, sk, ## args) #define SEND_generic(res, sk, args...) \ SEND_wrap(512, pktbuf_write_generic, res, sk, ## args) #define SEND_ReadyForQuery(res, sk) \ SEND_wrap(8, pktbuf_write_ReadyForQuery, res, sk) #define SEND_CancelRequest(res, sk, key) \ SEND_wrap(16, pktbuf_write_CancelRequest, res, sk, key) #define SEND_PasswordMessage(res, sk, psw) \ SEND_wrap(512, pktbuf_write_PasswordMessage, res, sk, psw) pgbouncer-1.7/include/objects.h0000664000175000017500000000705612511203334013517 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ extern struct StatList user_list; extern struct AATree user_tree; extern struct StatList pool_list; extern struct StatList database_list; extern struct StatList autodatabase_idle_list; extern struct StatList login_client_list; extern struct Slab *client_cache; extern struct Slab *server_cache; extern struct Slab *db_cache; extern struct Slab *pool_cache; extern struct Slab *user_cache; extern struct Slab *iobuf_cache; PgDatabase *find_database(const char *name); PgUser *find_user(const char *name); PgPool *get_pool(PgDatabase *, PgUser *); PgSocket *compare_connections_by_time(PgSocket *lhs, PgSocket *rhs); bool evict_connection(PgDatabase *db) _MUSTCHECK; bool evict_user_connection(PgUser *user) _MUSTCHECK; bool find_server(PgSocket *client) _MUSTCHECK; bool release_server(PgSocket *server) /* _MUSTCHECK */; bool finish_client_login(PgSocket *client) _MUSTCHECK; bool check_fast_fail(PgSocket *client) _MUSTCHECK; PgSocket *accept_client(int sock, bool is_unix) _MUSTCHECK; void disconnect_server(PgSocket *server, bool notify, const char *reason, ...) _PRINTF(3, 4); void disconnect_client(PgSocket *client, bool notify, const char *reason, ...) _PRINTF(3, 4); PgDatabase * add_database(const char *name) _MUSTCHECK; PgDatabase *register_auto_database(const char *name); PgUser * add_user(const char *name, const char *passwd) _MUSTCHECK; PgUser * add_db_user(PgDatabase *db, const char *name, const char *passwd) _MUSTCHECK; PgUser * force_user(PgDatabase *db, const char *username, const char *passwd) _MUSTCHECK; void accept_cancel_request(PgSocket *req); void forward_cancel_request(PgSocket *server); void launch_new_connection(PgPool *pool); bool use_client_socket(int fd, PgAddr *addr, const char *dbname, const char *username, uint64_t ckey, int oldfd, int linkfd, const char *client_end, const char *std_string, const char *datestyle, const char *timezone, const char *password) _MUSTCHECK; bool use_server_socket(int fd, PgAddr *addr, const char *dbname, const char *username, uint64_t ckey, int oldfd, int linkfd, const char *client_end, const char *std_string, const char *datestyle, const char *timezone, const char *password) _MUSTCHECK; void activate_client(PgSocket *client); void change_client_state(PgSocket *client, SocketState newstate); void change_server_state(PgSocket *server, SocketState newstate); int get_active_client_count(void); int get_active_server_count(void); void tag_database_dirty(PgDatabase *db); void tag_autodb_dirty(void); void tag_host_addr_dirty(const char *host, const struct sockaddr *sa); void for_each_server(PgPool *pool, void (*func)(PgSocket *sk)); void reuse_just_freed_objects(void); void init_objects(void); void init_caches(void); pgbouncer-1.7/include/iobuf.h0000664000175000017500000000703412572127367013210 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Temporary buffer for single i/o. * * Pattern: * * iobuf_get_and_reset() * start: * iobuf_recv() * loop: * if (new_pkt) * iobuf_parse() * * if (send) { * iobuf_tag_send() * } else { * send_pending() * iobuf_tag_skip() * } * if (more-unparsed) * goto loop; * send_pending(); */ /* * 0 .. done_pos -- sent * done_pos .. parse_pos -- parsed, to send * parse_pos .. recv_pos -- received, to parse */ struct iobuf { unsigned done_pos; unsigned parse_pos; unsigned recv_pos; uint8_t buf[FLEX_ARRAY]; }; typedef struct iobuf IOBuf; static inline bool iobuf_sane(const IOBuf *io) { return (io == NULL) || ( io->parse_pos >= io->done_pos && io->recv_pos >= io->parse_pos && (unsigned)cf_sbuf_len >= io->recv_pos); } static inline bool iobuf_empty(const IOBuf *io) { return io == NULL || io->done_pos == io->recv_pos; } /* unsent amount */ static inline unsigned iobuf_amount_pending(const IOBuf *buf) { return buf->parse_pos - buf->done_pos; } /* max possible to parse (tag_send/tag_skip) */ static inline unsigned iobuf_amount_parse(const IOBuf *buf) { return buf->recv_pos - buf->parse_pos; } /* max possible to recv */ static inline unsigned iobuf_amount_recv(const IOBuf *buf) { return cf_sbuf_len - buf->recv_pos; } /* put all unparsed to mbuf */ static inline unsigned iobuf_parse_all(const IOBuf *buf, struct MBuf *mbuf) { unsigned avail = iobuf_amount_parse(buf); const uint8_t *pos = buf->buf + buf->parse_pos; mbuf_init_fixed_reader(mbuf, pos, avail); return avail; } /* put all unparsed to mbuf, with size limit */ static inline unsigned iobuf_parse_limit(const IOBuf *buf, struct MBuf *mbuf, unsigned limit) { unsigned avail = iobuf_amount_parse(buf); const uint8_t *pos = buf->buf + buf->parse_pos; if (avail > limit) avail = limit; mbuf_init_fixed_reader(mbuf, pos, avail); return avail; } static inline void iobuf_tag_send(IOBuf *io, unsigned len) { Assert(len > 0 && len <= iobuf_amount_parse(io)); io->parse_pos += len; } static inline void iobuf_tag_skip(IOBuf *io, unsigned len) { Assert(io->parse_pos == io->done_pos); /* no send pending */ Assert(len > 0 && len <= iobuf_amount_parse(io)); io->parse_pos += len; io->done_pos = io->parse_pos; } static inline void iobuf_try_resync(IOBuf *io, unsigned small_pkt) { unsigned avail = io->recv_pos - io->done_pos; if (avail == 0) { if (io->recv_pos > 0) io->recv_pos = io->parse_pos = io->done_pos = 0; } else if (avail <= small_pkt && io->done_pos > 0) { memmove(io->buf, io->buf + io->done_pos, avail); io->parse_pos -= io->done_pos; io->recv_pos = avail; io->done_pos = 0; } } static inline void iobuf_reset(IOBuf *io) { io->recv_pos = io->parse_pos = io->done_pos = 0; } pgbouncer-1.7/include/proto.h0000664000175000017500000000510212572127367013241 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* old style V2 header: len:4b code:4b */ #define OLD_HEADER_LEN 8 /* new style V3 packet header len - type:1b, len:4b */ #define NEW_HEADER_LEN 5 /* * parsed packet header, plus whatever data is * available in SBuf for this packet. * * if (pkt->len == mbuf_avail(&pkt->data)) * packet is fully in buffer * * get_header() points pkt->data.pos after header. * to packet body. */ struct PktHdr { unsigned type; unsigned len; struct MBuf data; }; bool get_header(struct MBuf *data, PktHdr *pkt) _MUSTCHECK; bool send_pooler_error(PgSocket *client, bool send_ready, const char *msg) /*_MUSTCHECK*/; void log_server_error(const char *note, PktHdr *pkt); void parse_server_error(PktHdr *pkt, const char **level_p, const char **msg_p); 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; bool answer_authreq(PgSocket *server, PktHdr *pkt) _MUSTCHECK; bool send_startup_packet(PgSocket *server) _MUSTCHECK; bool send_sslreq_packet(PgSocket *server) _MUSTCHECK; int scan_text_result(struct MBuf *pkt, const char *tupdesc, ...) _MUSTCHECK; /* is packet completely in our buffer */ static inline bool incomplete_pkt(const PktHdr *pkt) { return mbuf_written(&pkt->data) != pkt->len; } /* is packet header completely in buffer */ static inline bool incomplete_header(const struct MBuf *data) { uint32_t avail = mbuf_avail_for_read(data); if (avail >= OLD_HEADER_LEN) return false; if (avail < NEW_HEADER_LEN) return true; /* is it old V2 header? */ return data->data[data->read_pos] == 0; } /* one char desc */ static inline char pkt_desc(const PktHdr *pkt) { return pkt->type > 256 ? '!' : pkt->type; } pgbouncer-1.7/include/client.h0000664000175000017500000000215312511203334013335 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ bool client_proto(SBuf *sbuf, SBufEvent evtype, struct MBuf *pkt) _MUSTCHECK; bool set_pool(PgSocket *client, const char *dbname, const char *username, const char *password, bool takeover) _MUSTCHECK; bool handle_auth_response(PgSocket *client, PktHdr *pkt); pgbouncer-1.7/include/sbuf.h0000664000175000017500000001071112630264362013027 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * event types for protocol handler */ typedef enum { SBUF_EV_READ, /* got new packet */ SBUF_EV_RECV_FAILED, /* error */ SBUF_EV_SEND_FAILED, /* error */ SBUF_EV_CONNECT_FAILED, /* error */ SBUF_EV_CONNECT_OK, /* got connection */ SBUF_EV_FLUSH, /* data is sent, buffer empty */ SBUF_EV_PKT_CALLBACK, /* next part of pkt data */ SBUF_EV_TLS_READY /* TLS was established */ } SBufEvent; /* * If less than this amount of data is pending, then * prefer to merge it with next recv(). * * It needs to be larger than data handler wants * to see completely. Generally just header, * but currently also ServerParam pkt. */ #define SBUF_SMALL_PKT 64 struct tls; /* fwd def */ typedef struct SBuf SBuf; typedef struct SBufIO SBufIO; /* callback should return true if it used one of sbuf_prepare_* on sbuf, false if it used sbuf_pause(), sbuf_close() or simply wants to wait for next event loop (eg. too few data available). */ typedef bool (*sbuf_cb_t)(SBuf *sbuf, SBufEvent evtype, struct MBuf *mbuf); /* for some reason, libevent has no typedef for callback */ typedef void (*sbuf_libevent_cb)(int, short, void *); struct SBufIO { int (*sbufio_recv)(SBuf *sbuf, void *buf, unsigned int len); int (*sbufio_send)(SBuf *sbuf, const void *data, unsigned int len); int (*sbufio_close)(SBuf *sbuf); }; /* * Stream Buffer. * * Stream is divided to packets. On each packet start * protocol handler is called that decides what to do. */ struct SBuf { struct event ev; /* libevent handle */ uint8_t wait_type; /* track wait state */ uint8_t pkt_action; /* method for handling current pkt */ uint8_t tls_state; /* progress of tls */ int sock; /* fd for this socket */ unsigned pkt_remain; /* total packet length remaining */ sbuf_cb_t proto_cb; /* protocol callback */ SBuf *dst; /* target SBuf for current packet */ IOBuf *io; /* data buffer, lazily allocated */ const SBufIO *ops; /* normal vs. TLS */ struct tls *tls; /* TLS context */ const char *tls_host; /* target hostname */ }; #define sbuf_socket(sbuf) ((sbuf)->sock) void sbuf_init(SBuf *sbuf, sbuf_cb_t proto_fn); bool sbuf_accept(SBuf *sbuf, int read_sock, bool is_unix) _MUSTCHECK; bool sbuf_connect(SBuf *sbuf, const struct sockaddr *sa, int sa_len, int timeout_sec) _MUSTCHECK; void sbuf_tls_setup(void); bool sbuf_tls_accept(SBuf *sbuf) _MUSTCHECK; bool sbuf_tls_connect(SBuf *sbuf, const char *hostname) _MUSTCHECK; bool sbuf_pause(SBuf *sbuf) _MUSTCHECK; void sbuf_continue(SBuf *sbuf); bool sbuf_close(SBuf *sbuf) _MUSTCHECK; /* proto_fn can use those functions to order behaviour */ void sbuf_prepare_send(SBuf *sbuf, SBuf *dst, unsigned amount); void sbuf_prepare_skip(SBuf *sbuf, unsigned amount); void sbuf_prepare_fetch(SBuf *sbuf, unsigned amount); bool sbuf_answer(SBuf *sbuf, const void *buf, unsigned len) _MUSTCHECK; bool sbuf_continue_with_callback(SBuf *sbuf, sbuf_libevent_cb cb) _MUSTCHECK; bool sbuf_use_callback_once(SBuf *sbuf, short ev, sbuf_libevent_cb user_cb) _MUSTCHECK; /* * Returns true if SBuf is has no data buffered * and is not in a middle of a packet. */ static inline bool sbuf_is_empty(SBuf *sbuf) { return iobuf_empty(sbuf->io) && sbuf->pkt_remain == 0; } static inline bool sbuf_is_closed(SBuf *sbuf) { return sbuf->sock == 0; } /* * Lowlevel operations. */ static inline int sbuf_op_recv(SBuf *sbuf, void *buf, unsigned int len) { return sbuf->ops->sbufio_recv(sbuf, buf, len); } static inline int sbuf_op_send(SBuf *sbuf, const void *buf, unsigned int len) { return sbuf->ops->sbufio_send(sbuf, buf, len); } static inline int sbuf_op_close(SBuf *sbuf) { return sbuf->ops->sbufio_close(sbuf); } pgbouncer-1.7/include/dnslookup.h0000664000175000017500000000355012511203334014077 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2010 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ struct DNSContext; struct DNSToken; struct addrinfo; typedef void (*adns_callback_f)(void *arg, const struct sockaddr *sa, int salen); struct DNSContext *adns_create_context(void); void adns_reload(struct DNSContext *ctx); void adns_free_context(struct DNSContext *ctx); struct DNSToken *adns_resolve(struct DNSContext *ctx, const char *name, adns_callback_f cb_func, void *arg); void adns_cancel(struct DNSContext *ctx, struct DNSToken *tk); const char *adns_get_backend(void); void adns_zone_cache_maint(struct DNSContext *ctx); void adns_info(struct DNSContext *ctx, int *names, int *zones, int *queries, int *pending); typedef void (*adns_walk_name_f)(void *arg, const char *name, const struct addrinfo *ai, usec_t ttl); typedef void (*adns_walk_zone_f)(void *arg, const char *name, uint32_t serial, int nhosts); void adns_walk_names(struct DNSContext *ctx, adns_walk_name_f cb, void *arg); void adns_walk_zones(struct DNSContext *ctx, adns_walk_zone_f cb, void *arg); void adns_per_loop(struct DNSContext *ctx); pgbouncer-1.7/include/janitor.h0000664000175000017500000000205412060213747013535 00000000000000/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ void janitor_setup(void); void config_postprocess(void); void resume_all(void); void per_loop_maint(void); bool suspend_socket(PgSocket *sk, bool force) _MUSTCHECK; void kill_pool(PgPool *pool); pgbouncer-1.7/etc/0000775000175000017500000000000012635051616011130 500000000000000pgbouncer-1.7/etc/pgbouncer.ini0000664000175000017500000001633412617620344013544 00000000000000;; database name = connect string ;; ;; connect string params: ;; dbname= host= port= user= password= ;; client_encoding= datestyle= timezone= ;; pool_size= connect_query= [databases] ; foodb over unix socket ;foodb = ; redirect bardb to bazdb on localhost ;bardb = host=localhost dbname=bazdb ; access to dest database will go with single user ;forcedb = host=127.0.0.1 port=300 user=baz password=foo client_encoding=UNICODE datestyle=ISO connect_query='SELECT 1' ; use custom pool sizes ;nondefaultdb = pool_size=50 reserve_pool_size=10 ; fallback connect string ;* = host=testserver ;; Configuration section [pgbouncer] ;;; ;;; Administrative settings ;;; logfile = /var/log/pgbouncer/pgbouncer.log pidfile = /var/run/pgbouncer/pgbouncer.pid ;;; ;;; Where to wait for clients ;;; ; ip address or * which means all ip-s listen_addr = 127.0.0.1 listen_port = 6432 ; unix socket is also used for -R. ; On debian it should be /var/run/postgresql ;unix_socket_dir = /tmp ;unix_socket_mode = 0777 ;unix_socket_group = ;;; ;;; Authentication settings ;;; ; any, trust, plain, crypt, md5 auth_type = trust ;auth_file = /8.0/main/global/pg_auth auth_file = /etc/pgbouncer/userlist.txt ;; Query to use to fetch password from database. Result ;; must have 2 columns - username and password hash. ;auth_query = SELECT usename, passwd FROM pg_shadow WHERE usename=$1 ;;; ;;; Users allowed into database 'pgbouncer' ;;; ; comma-separated list of users, who are allowed to change settings ;admin_users = user2, someadmin, otheradmin ; comma-separated list of users who are just allowed to use SHOW command ;stats_users = stats, root ;;; ;;; Pooler personality questions ;;; ; When server connection is released back to pool: ; session - after client disconnects ; transaction - after transaction finishes ; statement - after statement finishes pool_mode = session ; ; Query for cleaning connection immediately after releasing from client. ; No need to put ROLLBACK here, pgbouncer does not reuse connections ; where transaction is left open. ; ; Query for 8.3+: ; DISCARD ALL; ; ; Older versions: ; RESET ALL; SET SESSION AUTHORIZATION DEFAULT ; ; Empty if transaction pooling is in use. ; server_reset_query = DISCARD ALL ; Whether server_reset_query should run in all pooling modes. ; If it is off, server_reset_query is used only for session-pooling. ;server_reset_query_always = 0 ; ; Comma-separated list of parameters to ignore when given ; in startup packet. Newer JDBC versions require the ; extra_float_digits here. ; ;ignore_startup_parameters = extra_float_digits ; ; When taking idle server into use, this query is ran first. ; SELECT 1 ; ;server_check_query = select 1 ; If server was used more recently that this many seconds ago, ; skip the check query. Value 0 may or may not run in immediately. ;server_check_delay = 30 ;; Use as application_name on server. ;application_name_add_host = 0 ;;; ;;; Connection limits ;;; ; total number of clients that can connect max_client_conn = 100 ; default pool size. 20 is good number when transaction pooling ; is in use, in session pooling it needs to be the number of ; max clients you want to handle at any moment default_pool_size = 20 ;; Minimum number of server connections to keep in pool. ;min_pool_size = 0 ; how many additional connection to allow in case of trouble ;reserve_pool_size = 5 ; if a clients needs to wait more than this many seconds, use reserve pool ;reserve_pool_timeout = 3 ; how many total connections to a single database to allow from all pools ;max_db_connections = 50 ;max_user_connections = 50 ; If off, then server connections are reused in LIFO manner ;server_round_robin = 0 ;;; ;;; Logging ;;; ;; Syslog settings ;syslog = 0 ;syslog_facility = daemon ;syslog_ident = pgbouncer ; log if client connects or server connection is made ;log_connections = 1 ; log if and why connection was closed ;log_disconnections = 1 ; log error messages pooler sends to clients ;log_pooler_errors = 1 ;; Period for writing aggregated stats into log. ;stats_period = 60 ;; Logging verbosity. Same as -v switch on command line. ;verbose=0 ;;; ;;; Timeouts ;;; ;; Close server connection if its been connected longer. ;server_lifetime = 1200 ;; Close server connection if its not been used in this time. ;; Allows to clean unnecessary connections from pool after peak. ;server_idle_timeout = 60 ;; Cancel connection attempt if server does not answer takes longer. ;server_connect_timeout = 15 ;; If server login failed (server_connect_timeout or auth failure) ;; then wait this many second. ;server_login_retry = 15 ;; Dangerous. Server connection is closed if query does not return ;; in this time. Should be used to survive network problems, ;; _not_ as statement_timeout. (default: 0) ;query_timeout = 0 ;; Dangerous. Client connection is closed if the query is not assigned ;; to a server in this time. Should be used to limit the number of queued ;; queries in case of a database or network failure. (default: 120) ;query_wait_timeout = 120 ;; Dangerous. Client connection is closed if no activity in this time. ;; Should be used to survive network problems. (default: 0) ;client_idle_timeout = 0 ;; Disconnect clients who have not managed to log in after connecting ;; in this many seconds. ;client_login_timeout = 60 ;; Clean automatically created database entries (via "*") if they ;; stay unused in this many seconds. ; autodb_idle_timeout = 3600 ;; How long SUSPEND/-R waits for buffer flush before closing connection. ;suspend_timeout = 10 ;; Close connections which are in "IDLE in transaction" state longer than ;; this many seconds. ;idle_transaction_timeout = 0 ;;; ;;; Low-level tuning options ;;; ;; buffer for streaming packets ;pkt_buf = 4096 ;; man 2 listen ;listen_backlog = 128 ;; Max number pkt_buf to process in one event loop. ;sbuf_loopcnt = 5 ;; Maximum Postgres protocol packet size. ;max_packet_size = 2147483647 ;; networking options, for info: man 7 tcp ;; Linux: notify program about new connection only if there ;; is also data received. (Seconds to wait.) ;; On Linux the default is 45, on other OS'es 0. ;tcp_defer_accept = 0 ;; In-kernel buffer size (Linux default: 4096) ;tcp_socket_buffer = 0 ;; whether tcp keepalive should be turned on (0/1) ;tcp_keepalive = 1 ;; following options are Linux-specific. ;; they also require tcp_keepalive=1 ;; count of keepaliva packets ;tcp_keepcnt = 0 ;; how long the connection can be idle, ;; before sending keepalive packets ;tcp_keepidle = 0 ;; The time between individual keepalive probes. ;tcp_keepintvl = 0 ;; DNS lookup caching time ;dns_max_ttl = 15 ;; DNS zone SOA lookup period ;dns_zone_check_period = 0 ;; DNS negative result caching time ;dns_nxdomain_ttl = 15 ;;; ;;; Random stuff ;;; ;; Hackish security feature. Helps against SQL-injection - when PQexec is disabled, ;; multi-statement cannot be made. ;disable_pqexec=0 ;; Config file to use for next RELOAD/SIGHUP. ;; By default contains config file from command line. ;conffile ;; Win32 service name to register as. job_name is alias for service_name, ;; used by some Skytools scripts. ;service_name = pgbouncer ;job_name = pgbouncer ;; Read additional config from the /etc/pgbouncer/pgbouncer-other.ini file ;%include /etc/pgbouncer/pgbouncer-other.ini pgbouncer-1.7/etc/userlist.txt0000664000175000017500000000007012060213747013455 00000000000000"marko" "asdasd" "postgres" "asdasd" "pgbouncer" "fake" pgbouncer-1.7/etc/example.debian.init.sh0000664000175000017500000000205112060213747015215 00000000000000#!/bin/bash # # pgbouncer Start the PgBouncer PostgreSQL pooler. # # The variables below are NOT to be changed. They are there to make the # script more readable. NAME=pgbouncer DAEMON=/usr/bin/$NAME PIDFILE=/var/run/$NAME.pid CONF=/etc/$NAME.ini OPTS="-d $CONF" # note: SSD is required only at startup of the daemon. SSD=`which start-stop-daemon` ENV="env -i LANG=C PATH=/bin:/usr/bin:/usr/local/bin" trap "" 1 # Check if configuration exists test -f $CONF || exit 0 case "$1" in start) echo -n "Starting server: $NAME" $ENV $SSD --start --pidfile $PIDFILE --exec $DAEMON -- $OPTS > /dev/null ;; stop) echo -n "Stopping server: $NAME" start-stop-daemon --stop --pidfile $PIDFILE ;; reload | force-reload) echo -n "Reloading $NAME configuration" start-stop-daemon --stop --pidfile $PIDFILE --signal HUP ;; restart) $0 stop $0 start ;; *) echo "Usage: /etc/init.d/$NAME {start|stop|reload|restart}" exit 1 ;; esac if [ $? -eq 0 ]; then echo . exit 0 else echo " failed" exit 1 fi pgbouncer-1.7/etc/mkauth.py0000775000175000017500000000143612060213747012717 00000000000000#! /usr/bin/env python import sys, os, tempfile, psycopg2 if len(sys.argv) != 3: print 'usage: mkauth DSTFN CONNSTR' sys.exit(1) # read old file fn = sys.argv[1] try: old = open(fn, 'r').read() except IOError: old = '' # create new file data db = psycopg2.connect(sys.argv[2]) curs = db.cursor() curs.execute("select usename, passwd from pg_shadow order by 1") lines = [] for user, psw in curs.fetchall(): user = user.replace('"', '""') if not psw: psw = '' psw = psw.replace('"', '""') lines.append('"%s" "%s" ""\n' % (user, psw)) db.commit() cur = "".join(lines) # if changed, replace data securely if old != cur: fd, tmpfn = tempfile.mkstemp(dir = os.path.split(fn)[0]) f = os.fdopen(fd, 'w') f.write(cur) f.close() os.rename(tmpfn, fn) pgbouncer-1.7/AUTHORS0000664000175000017500000000137212627600727011354 00000000000000 Maintainers ----------- Marko Kreen Petr Jelinek Contributors ------------ Alexander Schöcke Andrew Dunstan Bjoern Metzdorf Bob Poekert Christoph Berg Cody Cutrer Dan McGee David Fetter David Galoyan David Sommerseth Devrim GÜNDÜZ Dimitri Fontaine Dmitriy Olshevskiy Dominique Hermsdorff Emmanuel Courreges Eric Radman Euler Taveira Filip Rembiałkowski Giorgio Valoti Guillaume Aubert Guillaume Lelarge Greg Sabino Mullane Hannu Krosing Heikki Linnakangas Hiroshi Saito Hubert Depesz Lubaczewski Jacob Coby James Pye Jørgen Austvik Lou Picciano Magne Mæhre Magnus Hagander Martin Pihlak Mathieu Fenniak Michael Tharp Michał Trojnara Pavel Stehule Peter Eisentraut Pierre-Emmanuel André Rich Schaaf Robert Gogolok Sam McLeod Teodor Sigaev William Grant pgbouncer-1.7/config.sub0000755000175000017500000010517612635051441012264 00000000000000#! /bin/sh # Configuration validation subroutine script. # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, # 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, # 2011, 2012 Free Software Foundation, Inc. timestamp='2012-02-10' # This file is (in principle) common to ALL GNU software. # The presence of a machine in this file suggests that SOME GNU software # can handle that machine. It does not imply ALL GNU software can. # # This file 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. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Please send patches to . Submit a context # diff and a properly formatted GNU ChangeLog entry. # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. # If it is invalid, we print an error message on stderr and exit with code 1. # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases # that are meaningful with *any* GNU software. # Each package is responsible for reporting which valid configurations # it does not support. The user should be able to distinguish # a failure to support a valid configuration from a meaningless # configuration. # The goal of this file is to map all the various variations of a given # machine specification into a single specification in the form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or in some cases, the newer four-part form: # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] CPU-MFR-OPSYS $0 [OPTION] ALIAS Canonicalize a configuration name. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.sub ($timestamp) Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" exit 1 ;; *local*) # First pass through any local machine types. echo $1 exit ;; * ) break ;; esac done case $# in 0) echo "$me: missing argument$help" >&2 exit 1;; 1) ;; *) echo "$me: too many arguments$help" >&2 exit 1;; esac # Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). # Here we must recognize all the valid KERNEL-OS combinations. maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` case $maybe_os in nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ knetbsd*-gnu* | netbsd*-gnu* | \ kopensolaris*-gnu* | \ storm-chaos* | os2-emx* | rtmk-nova*) os=-$maybe_os basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` ;; android-linux) os=-linux-android basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown ;; *) basic_machine=`echo $1 | sed 's/-[^-]*$//'` if [ $basic_machine != $1 ] then os=`echo $1 | sed 's/.*-/-/'` else os=; fi ;; esac ### Let's recognize common machines as not being operating systems so ### that things like config.sub decstation-3100 work. We also ### recognize some manufacturers as not being operating systems, so we ### can provide default operating systems below. case $os in -sun*os*) # Prevent following clause from handling this invalid input. ;; -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ -apple | -axis | -knuth | -cray | -microblaze) os= basic_machine=$1 ;; -bluegene*) os=-cnk ;; -sim | -cisco | -oki | -wec | -winbond) os= basic_machine=$1 ;; -scout) ;; -wrs) os=-vxworks basic_machine=$1 ;; -chorusos*) os=-chorusos basic_machine=$1 ;; -chorusrdb) os=-chorusrdb basic_machine=$1 ;; -hiux*) os=-hiuxwe2 ;; -sco6) os=-sco5v6 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco5) os=-sco3.2v5 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco4) os=-sco3.2v4 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco3.2.[4-9]*) os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco3.2v[4-9]*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco5v6*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco*) os=-sco3.2v2 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -udk*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -isc) os=-isc2.2 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -clix*) basic_machine=clipper-intergraph ;; -isc*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -lynx*) os=-lynxos ;; -ptx*) basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` ;; -windowsnt*) os=`echo $os | sed -e 's/windowsnt/winnt/'` ;; -psos*) os=-psos ;; -mint | -mint[0-9]*) basic_machine=m68k-atari os=-mint ;; esac # Decode aliases for certain CPU-COMPANY combinations. case $basic_machine in # Recognize the basic CPU types without company name. # Some are omitted here because they have special meanings below. 1750a | 580 \ | a29k \ | aarch64 | aarch64_be \ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ | am33_2.0 \ | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \ | be32 | be64 \ | bfin \ | c4x | clipper \ | d10v | d30v | dlx | dsp16xx \ | epiphany \ | fido | fr30 | frv \ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | hexagon \ | i370 | i860 | i960 | ia64 \ | ip2k | iq2000 \ | le32 | le64 \ | lm32 \ | m32c | m32r | m32rle | m68000 | m68k | m88k \ | maxq | mb | microblaze | mcore | mep | metag \ | mips | mipsbe | mipseb | mipsel | mipsle \ | mips16 \ | mips64 | mips64el \ | mips64octeon | mips64octeonel \ | mips64orion | mips64orionel \ | mips64r5900 | mips64r5900el \ | mips64vr | mips64vrel \ | mips64vr4100 | mips64vr4100el \ | mips64vr4300 | mips64vr4300el \ | mips64vr5000 | mips64vr5000el \ | mips64vr5900 | mips64vr5900el \ | mipsisa32 | mipsisa32el \ | mipsisa32r2 | mipsisa32r2el \ | mipsisa64 | mipsisa64el \ | mipsisa64r2 | mipsisa64r2el \ | mipsisa64sb1 | mipsisa64sb1el \ | mipsisa64sr71k | mipsisa64sr71kel \ | mipstx39 | mipstx39el \ | mn10200 | mn10300 \ | moxie \ | mt \ | msp430 \ | nds32 | nds32le | nds32be \ | nios | nios2 \ | ns16k | ns32k \ | open8 \ | or32 \ | pdp10 | pdp11 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle \ | pyramid \ | rl78 | rx \ | score \ | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ | sh64 | sh64le \ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ | spu \ | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ | ubicom32 \ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ | we32k \ | x86 | xc16x | xstormy16 | xtensa \ | z8k | z80) basic_machine=$basic_machine-unknown ;; c54x) basic_machine=tic54x-unknown ;; c55x) basic_machine=tic55x-unknown ;; c6x) basic_machine=tic6x-unknown ;; m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | picochip) basic_machine=$basic_machine-unknown os=-none ;; m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) ;; ms1) basic_machine=mt-unknown ;; strongarm | thumb | xscale) basic_machine=arm-unknown ;; xgate) basic_machine=$basic_machine-unknown os=-none ;; xscaleeb) basic_machine=armeb-unknown ;; xscaleel) basic_machine=armel-unknown ;; # We use `pc' rather than `unknown' # because (1) that's what they normally are, and # (2) the word "unknown" tends to confuse beginning users. i*86 | x86_64) basic_machine=$basic_machine-pc ;; # Object if more than one company name word. *-*-*) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; # Recognize the basic CPU types with company name. 580-* \ | a29k-* \ | aarch64-* | aarch64_be-* \ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ | avr-* | avr32-* \ | be32-* | be64-* \ | bfin-* | bs2000-* \ | c[123]* | c30-* | [cjt]90-* | c4x-* \ | clipper-* | craynv-* | cydra-* \ | d10v-* | d30v-* | dlx-* \ | elxsi-* \ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ | h8300-* | h8500-* \ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ | hexagon-* \ | i*86-* | i860-* | i960-* | ia64-* \ | ip2k-* | iq2000-* \ | le32-* | le64-* \ | lm32-* \ | m32c-* | m32r-* | m32rle-* \ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ | m88110-* | m88k-* | maxq-* | mcore-* | metag-* | microblaze-* \ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ | mips16-* \ | mips64-* | mips64el-* \ | mips64octeon-* | mips64octeonel-* \ | mips64orion-* | mips64orionel-* \ | mips64r5900-* | mips64r5900el-* \ | mips64vr-* | mips64vrel-* \ | mips64vr4100-* | mips64vr4100el-* \ | mips64vr4300-* | mips64vr4300el-* \ | mips64vr5000-* | mips64vr5000el-* \ | mips64vr5900-* | mips64vr5900el-* \ | mipsisa32-* | mipsisa32el-* \ | mipsisa32r2-* | mipsisa32r2el-* \ | mipsisa64-* | mipsisa64el-* \ | mipsisa64r2-* | mipsisa64r2el-* \ | mipsisa64sb1-* | mipsisa64sb1el-* \ | mipsisa64sr71k-* | mipsisa64sr71kel-* \ | mipstx39-* | mipstx39el-* \ | mmix-* \ | mt-* \ | msp430-* \ | nds32-* | nds32le-* | nds32be-* \ | nios-* | nios2-* \ | none-* | np1-* | ns16k-* | ns32k-* \ | open8-* \ | orion-* \ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ | pyramid-* \ | rl78-* | romp-* | rs6000-* | rx-* \ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ | sparclite-* \ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \ | tahoe-* \ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ | tile*-* \ | tron-* \ | ubicom32-* \ | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ | vax-* \ | we32k-* \ | x86-* | x86_64-* | xc16x-* | xps100-* \ | xstormy16-* | xtensa*-* \ | ymp-* \ | z8k-* | z80-*) ;; # Recognize the basic CPU types without company name, with glob match. xtensa*) basic_machine=$basic_machine-unknown ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. 386bsd) basic_machine=i386-unknown os=-bsd ;; 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) basic_machine=m68000-att ;; 3b*) basic_machine=we32k-att ;; a29khif) basic_machine=a29k-amd os=-udi ;; abacus) basic_machine=abacus-unknown ;; adobe68k) basic_machine=m68010-adobe os=-scout ;; alliant | fx80) basic_machine=fx80-alliant ;; altos | altos3068) basic_machine=m68k-altos ;; am29k) basic_machine=a29k-none os=-bsd ;; amd64) basic_machine=x86_64-pc ;; amd64-*) basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; amdahl) basic_machine=580-amdahl os=-sysv ;; amiga | amiga-*) basic_machine=m68k-unknown ;; amigaos | amigados) basic_machine=m68k-unknown os=-amigaos ;; amigaunix | amix) basic_machine=m68k-unknown os=-sysv4 ;; apollo68) basic_machine=m68k-apollo os=-sysv ;; apollo68bsd) basic_machine=m68k-apollo os=-bsd ;; aros) basic_machine=i386-pc os=-aros ;; aux) basic_machine=m68k-apple os=-aux ;; balance) basic_machine=ns32k-sequent os=-dynix ;; blackfin) basic_machine=bfin-unknown os=-linux ;; blackfin-*) basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; bluegene*) basic_machine=powerpc-ibm os=-cnk ;; c54x-*) basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c55x-*) basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c6x-*) basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c90) basic_machine=c90-cray os=-unicos ;; cegcc) basic_machine=arm-unknown os=-cegcc ;; convex-c1) basic_machine=c1-convex os=-bsd ;; convex-c2) basic_machine=c2-convex os=-bsd ;; convex-c32) basic_machine=c32-convex os=-bsd ;; convex-c34) basic_machine=c34-convex os=-bsd ;; convex-c38) basic_machine=c38-convex os=-bsd ;; cray | j90) basic_machine=j90-cray os=-unicos ;; craynv) basic_machine=craynv-cray os=-unicosmp ;; cr16 | cr16-*) basic_machine=cr16-unknown os=-elf ;; crds | unos) basic_machine=m68k-crds ;; crisv32 | crisv32-* | etraxfs*) basic_machine=crisv32-axis ;; cris | cris-* | etrax*) basic_machine=cris-axis ;; crx) basic_machine=crx-unknown os=-elf ;; da30 | da30-*) basic_machine=m68k-da30 ;; decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) basic_machine=mips-dec ;; decsystem10* | dec10*) basic_machine=pdp10-dec os=-tops10 ;; decsystem20* | dec20*) basic_machine=pdp10-dec os=-tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) basic_machine=m68k-motorola ;; delta88) basic_machine=m88k-motorola os=-sysv3 ;; dicos) basic_machine=i686-pc os=-dicos ;; djgpp) basic_machine=i586-pc os=-msdosdjgpp ;; dpx20 | dpx20-*) basic_machine=rs6000-bull os=-bosx ;; dpx2* | dpx2*-bull) basic_machine=m68k-bull os=-sysv3 ;; ebmon29k) basic_machine=a29k-amd os=-ebmon ;; elxsi) basic_machine=elxsi-elxsi os=-bsd ;; encore | umax | mmax) basic_machine=ns32k-encore ;; es1800 | OSE68k | ose68k | ose | OSE) basic_machine=m68k-ericsson os=-ose ;; fx2800) basic_machine=i860-alliant ;; genix) basic_machine=ns32k-ns ;; gmicro) basic_machine=tron-gmicro os=-sysv ;; go32) basic_machine=i386-pc os=-go32 ;; h3050r* | hiux*) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; h8300hms) basic_machine=h8300-hitachi os=-hms ;; h8300xray) basic_machine=h8300-hitachi os=-xray ;; h8500hms) basic_machine=h8500-hitachi os=-hms ;; harris) basic_machine=m88k-harris os=-sysv3 ;; hp300-*) basic_machine=m68k-hp ;; hp300bsd) basic_machine=m68k-hp os=-bsd ;; hp300hpux) basic_machine=m68k-hp os=-hpux ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) basic_machine=m68000-hp ;; hp9k3[2-9][0-9]) basic_machine=m68k-hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) basic_machine=hppa1.1-hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) basic_machine=hppa1.1-hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) basic_machine=hppa1.0-hp ;; hppa-next) os=-nextstep3 ;; hppaosf) basic_machine=hppa1.1-hp os=-osf ;; hppro) basic_machine=hppa1.1-hp os=-proelf ;; i370-ibm* | ibm*) basic_machine=i370-ibm ;; i*86v32) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv32 ;; i*86v4*) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv4 ;; i*86v) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv ;; i*86sol2) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-solaris2 ;; i386mach) basic_machine=i386-mach os=-mach ;; i386-vsta | vsta) basic_machine=i386-unknown os=-vsta ;; iris | iris4d) basic_machine=mips-sgi case $os in -irix*) ;; *) os=-irix4 ;; esac ;; isi68 | isi) basic_machine=m68k-isi os=-sysv ;; m68knommu) basic_machine=m68k-unknown os=-linux ;; m68knommu-*) basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; m88k-omron*) basic_machine=m88k-omron ;; magnum | m3230) basic_machine=mips-mips os=-sysv ;; merlin) basic_machine=ns32k-utek os=-sysv ;; microblaze) basic_machine=microblaze-xilinx ;; mingw32) basic_machine=i386-pc os=-mingw32 ;; mingw32ce) basic_machine=arm-unknown os=-mingw32ce ;; miniframe) basic_machine=m68000-convergent ;; *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) basic_machine=m68k-atari os=-mint ;; mips3*-*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` ;; mips3*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown ;; monitor) basic_machine=m68k-rom68k os=-coff ;; morphos) basic_machine=powerpc-unknown os=-morphos ;; msdos) basic_machine=i386-pc os=-msdos ;; ms1-*) basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` ;; msys) basic_machine=i386-pc os=-msys ;; mvs) basic_machine=i370-ibm os=-mvs ;; nacl) basic_machine=le32-unknown os=-nacl ;; ncr3000) basic_machine=i486-ncr os=-sysv4 ;; netbsd386) basic_machine=i386-unknown os=-netbsd ;; netwinder) basic_machine=armv4l-rebel os=-linux ;; news | news700 | news800 | news900) basic_machine=m68k-sony os=-newsos ;; news1000) basic_machine=m68030-sony os=-newsos ;; news-3600 | risc-news) basic_machine=mips-sony os=-newsos ;; necv70) basic_machine=v70-nec os=-sysv ;; next | m*-next ) basic_machine=m68k-next case $os in -nextstep* ) ;; -ns2*) os=-nextstep2 ;; *) os=-nextstep3 ;; esac ;; nh3000) basic_machine=m68k-harris os=-cxux ;; nh[45]000) basic_machine=m88k-harris os=-cxux ;; nindy960) basic_machine=i960-intel os=-nindy ;; mon960) basic_machine=i960-intel os=-mon960 ;; nonstopux) basic_machine=mips-compaq os=-nonstopux ;; np1) basic_machine=np1-gould ;; neo-tandem) basic_machine=neo-tandem ;; nse-tandem) basic_machine=nse-tandem ;; nsr-tandem) basic_machine=nsr-tandem ;; op50n-* | op60c-*) basic_machine=hppa1.1-oki os=-proelf ;; openrisc | openrisc-*) basic_machine=or32-unknown ;; os400) basic_machine=powerpc-ibm os=-os400 ;; OSE68000 | ose68000) basic_machine=m68000-ericsson os=-ose ;; os68k) basic_machine=m68k-none os=-os68k ;; pa-hitachi) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; paragon) basic_machine=i860-intel os=-osf ;; parisc) basic_machine=hppa-unknown os=-linux ;; parisc-*) basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; pbd) basic_machine=sparc-tti ;; pbb) basic_machine=m68k-tti ;; pc532 | pc532-*) basic_machine=ns32k-pc532 ;; pc98) basic_machine=i386-pc ;; pc98-*) basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentium | p5 | k5 | k6 | nexgen | viac3) basic_machine=i586-pc ;; pentiumpro | p6 | 6x86 | athlon | athlon_*) basic_machine=i686-pc ;; pentiumii | pentium2 | pentiumiii | pentium3) basic_machine=i686-pc ;; pentium4) basic_machine=i786-pc ;; pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentiumpro-* | p6-* | 6x86-* | athlon-*) basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentium4-*) basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pn) basic_machine=pn-gould ;; power) basic_machine=power-ibm ;; ppc | ppcbe) basic_machine=powerpc-unknown ;; ppc-* | ppcbe-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppcle | powerpclittle | ppc-le | powerpc-little) basic_machine=powerpcle-unknown ;; ppcle-* | powerpclittle-*) basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppc64) basic_machine=powerpc64-unknown ;; ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppc64le | powerpc64little | ppc64-le | powerpc64-little) basic_machine=powerpc64le-unknown ;; ppc64le-* | powerpc64little-*) basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ps2) basic_machine=i386-ibm ;; pw32) basic_machine=i586-unknown os=-pw32 ;; rdos) basic_machine=i386-pc os=-rdos ;; rom68k) basic_machine=m68k-rom68k os=-coff ;; rm[46]00) basic_machine=mips-siemens ;; rtpc | rtpc-*) basic_machine=romp-ibm ;; s390 | s390-*) basic_machine=s390-ibm ;; s390x | s390x-*) basic_machine=s390x-ibm ;; sa29200) basic_machine=a29k-amd os=-udi ;; sb1) basic_machine=mipsisa64sb1-unknown ;; sb1el) basic_machine=mipsisa64sb1el-unknown ;; sde) basic_machine=mipsisa32-sde os=-elf ;; sei) basic_machine=mips-sei os=-seiux ;; sequent) basic_machine=i386-sequent ;; sh) basic_machine=sh-hitachi os=-hms ;; sh5el) basic_machine=sh5le-unknown ;; sh64) basic_machine=sh64-unknown ;; sparclite-wrs | simso-wrs) basic_machine=sparclite-wrs os=-vxworks ;; sps7) basic_machine=m68k-bull os=-sysv2 ;; spur) basic_machine=spur-unknown ;; st2000) basic_machine=m68k-tandem ;; stratus) basic_machine=i860-stratus os=-sysv4 ;; strongarm-* | thumb-*) basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'` ;; sun2) basic_machine=m68000-sun ;; sun2os3) basic_machine=m68000-sun os=-sunos3 ;; sun2os4) basic_machine=m68000-sun os=-sunos4 ;; sun3os3) basic_machine=m68k-sun os=-sunos3 ;; sun3os4) basic_machine=m68k-sun os=-sunos4 ;; sun4os3) basic_machine=sparc-sun os=-sunos3 ;; sun4os4) basic_machine=sparc-sun os=-sunos4 ;; sun4sol2) basic_machine=sparc-sun os=-solaris2 ;; sun3 | sun3-*) basic_machine=m68k-sun ;; sun4) basic_machine=sparc-sun ;; sun386 | sun386i | roadrunner) basic_machine=i386-sun ;; sv1) basic_machine=sv1-cray os=-unicos ;; symmetry) basic_machine=i386-sequent os=-dynix ;; t3e) basic_machine=alphaev5-cray os=-unicos ;; t90) basic_machine=t90-cray os=-unicos ;; tile*) basic_machine=$basic_machine-unknown os=-linux-gnu ;; tx39) basic_machine=mipstx39-unknown ;; tx39el) basic_machine=mipstx39el-unknown ;; toad1) basic_machine=pdp10-xkl os=-tops20 ;; tower | tower-32) basic_machine=m68k-ncr ;; tpf) basic_machine=s390x-ibm os=-tpf ;; udi29k) basic_machine=a29k-amd os=-udi ;; ultra3) basic_machine=a29k-nyu os=-sym1 ;; v810 | necv810) basic_machine=v810-nec os=-none ;; vaxv) basic_machine=vax-dec os=-sysv ;; vms) basic_machine=vax-dec os=-vms ;; vpp*|vx|vx-*) basic_machine=f301-fujitsu ;; vxworks960) basic_machine=i960-wrs os=-vxworks ;; vxworks68) basic_machine=m68k-wrs os=-vxworks ;; vxworks29k) basic_machine=a29k-wrs os=-vxworks ;; w65*) basic_machine=w65-wdc os=-none ;; w89k-*) basic_machine=hppa1.1-winbond os=-proelf ;; xbox) basic_machine=i686-pc os=-mingw32 ;; xps | xps100) basic_machine=xps100-honeywell ;; xscale-* | xscalee[bl]-*) basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'` ;; ymp) basic_machine=ymp-cray os=-unicos ;; z8k-*-coff) basic_machine=z8k-unknown os=-sim ;; z80-*-coff) basic_machine=z80-unknown os=-sim ;; none) basic_machine=none-none os=-none ;; # Here we handle the default manufacturer of certain CPU types. It is in # some cases the only manufacturer, in others, it is the most popular. w89k) basic_machine=hppa1.1-winbond ;; op50n) basic_machine=hppa1.1-oki ;; op60c) basic_machine=hppa1.1-oki ;; romp) basic_machine=romp-ibm ;; mmix) basic_machine=mmix-knuth ;; rs6000) basic_machine=rs6000-ibm ;; vax) basic_machine=vax-dec ;; pdp10) # there are many clones, so DEC is not a safe bet basic_machine=pdp10-unknown ;; pdp11) basic_machine=pdp11-dec ;; we32k) basic_machine=we32k-att ;; sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) basic_machine=sh-unknown ;; sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) basic_machine=sparc-sun ;; cydra) basic_machine=cydra-cydrome ;; orion) basic_machine=orion-highlevel ;; orion105) basic_machine=clipper-highlevel ;; mac | mpw | mac-mpw) basic_machine=m68k-apple ;; pmac | pmac-mpw) basic_machine=powerpc-apple ;; *-unknown) # Make sure to match an already-canonicalized machine name. ;; *) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; esac # Here we canonicalize certain aliases for manufacturers. case $basic_machine in *-digital*) basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` ;; *-commodore*) basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` ;; *) ;; esac # Decode manufacturer-specific aliases for certain operating systems. if [ x"$os" != x"" ] then case $os in # First match some system type aliases # that might get confused with valid system types. # -solaris* is a basic system type, with this one exception. -auroraux) os=-auroraux ;; -solaris1 | -solaris1.*) os=`echo $os | sed -e 's|solaris1|sunos4|'` ;; -solaris) os=-solaris2 ;; -svr4*) os=-sysv4 ;; -unixware*) os=-sysv4.2uw ;; -gnu/linux*) os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` ;; # First accept the basic system types. # The portable systems comes first. # Each alternative MUST END IN A *, to match a version number. # -sysv* is not here because it comes later, after sysvr4. -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ | -sym* | -kopensolaris* \ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ | -aos* | -aros* \ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ | -openbsd* | -solidbsd* \ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ | -chorusos* | -chorusrdb* | -cegcc* \ | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ | -mingw32* | -linux-gnu* | -linux-android* \ | -linux-newlib* | -linux-uclibc* \ | -uxpv* | -beos* | -mpeix* | -udk* \ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*) # Remember, each alternative MUST END IN *, to match a version number. ;; -qnx*) case $basic_machine in x86-* | i*86-*) ;; *) os=-nto$os ;; esac ;; -nto-qnx*) ;; -nto*) os=`echo $os | sed -e 's|nto|nto-qnx|'` ;; -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) ;; -mac*) os=`echo $os | sed -e 's|mac|macos|'` ;; -linux-dietlibc) os=-linux-dietlibc ;; -linux*) os=`echo $os | sed -e 's|linux|linux-gnu|'` ;; -sunos5*) os=`echo $os | sed -e 's|sunos5|solaris2|'` ;; -sunos6*) os=`echo $os | sed -e 's|sunos6|solaris3|'` ;; -opened*) os=-openedition ;; -os400*) os=-os400 ;; -wince*) os=-wince ;; -osfrose*) os=-osfrose ;; -osf*) os=-osf ;; -utek*) os=-bsd ;; -dynix*) os=-bsd ;; -acis*) os=-aos ;; -atheos*) os=-atheos ;; -syllable*) os=-syllable ;; -386bsd) os=-bsd ;; -ctix* | -uts*) os=-sysv ;; -nova*) os=-rtmk-nova ;; -ns2 ) os=-nextstep2 ;; -nsk*) os=-nsk ;; # Preserve the version number of sinix5. -sinix5.*) os=`echo $os | sed -e 's|sinix|sysv|'` ;; -sinix*) os=-sysv4 ;; -tpf*) os=-tpf ;; -triton*) os=-sysv3 ;; -oss*) os=-sysv3 ;; -svr4) os=-sysv4 ;; -svr3) os=-sysv3 ;; -sysvr4) os=-sysv4 ;; # This must come after -sysvr4. -sysv*) ;; -ose*) os=-ose ;; -es1800*) os=-ose ;; -xenix) os=-xenix ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) os=-mint ;; -aros*) os=-aros ;; -kaos*) os=-kaos ;; -zvmoe) os=-zvmoe ;; -dicos*) os=-dicos ;; -nacl*) ;; -none) ;; *) # Get rid of the `-' at the beginning of $os. os=`echo $os | sed 's/[^-]*-//'` echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 exit 1 ;; esac else # Here we handle the default operating systems that come with various machines. # The value should be what the vendor currently ships out the door with their # machine or put another way, the most popular os provided with the machine. # Note that if you're going to try to match "-MANUFACTURER" here (say, # "-sun"), then you have to tell the case statement up towards the top # that MANUFACTURER isn't an operating system. Otherwise, code above # will signal an error saying that MANUFACTURER isn't an operating # system, and we'll never get to this point. case $basic_machine in score-*) os=-elf ;; spu-*) os=-elf ;; *-acorn) os=-riscix1.2 ;; arm*-rebel) os=-linux ;; arm*-semi) os=-aout ;; c4x-* | tic4x-*) os=-coff ;; tic54x-*) os=-coff ;; tic55x-*) os=-coff ;; tic6x-*) os=-coff ;; # This must come before the *-dec entry. pdp10-*) os=-tops20 ;; pdp11-*) os=-none ;; *-dec | vax-*) os=-ultrix4.2 ;; m68*-apollo) os=-domain ;; i386-sun) os=-sunos4.0.2 ;; m68000-sun) os=-sunos3 ;; m68*-cisco) os=-aout ;; mep-*) os=-elf ;; mips*-cisco) os=-elf ;; mips*-*) os=-elf ;; or32-*) os=-coff ;; *-tti) # must be before sparc entry or we get the wrong os. os=-sysv3 ;; sparc-* | *-sun) os=-sunos4.1.1 ;; *-be) os=-beos ;; *-haiku) os=-haiku ;; *-ibm) os=-aix ;; *-knuth) os=-mmixware ;; *-wec) os=-proelf ;; *-winbond) os=-proelf ;; *-oki) os=-proelf ;; *-hp) os=-hpux ;; *-hitachi) os=-hiux ;; i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) os=-sysv ;; *-cbm) os=-amigaos ;; *-dg) os=-dgux ;; *-dolphin) os=-sysv3 ;; m68k-ccur) os=-rtu ;; m88k-omron*) os=-luna ;; *-next ) os=-nextstep ;; *-sequent) os=-ptx ;; *-crds) os=-unos ;; *-ns) os=-genix ;; i370-*) os=-mvs ;; *-next) os=-nextstep3 ;; *-gould) os=-sysv ;; *-highlevel) os=-bsd ;; *-encore) os=-bsd ;; *-sgi) os=-irix ;; *-siemens) os=-sysv4 ;; *-masscomp) os=-rtu ;; f30[01]-fujitsu | f700-fujitsu) os=-uxpv ;; *-rom68k) os=-coff ;; *-*bug) os=-coff ;; *-apple) os=-macos ;; *-atari*) os=-mint ;; *) os=-none ;; esac fi # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. vendor=unknown case $basic_machine in *-unknown) case $os in -riscix*) vendor=acorn ;; -sunos*) vendor=sun ;; -cnk*|-aix*) vendor=ibm ;; -beos*) vendor=be ;; -hpux*) vendor=hp ;; -mpeix*) vendor=hp ;; -hiux*) vendor=hitachi ;; -unos*) vendor=crds ;; -dgux*) vendor=dg ;; -luna*) vendor=omron ;; -genix*) vendor=ns ;; -mvs* | -opened*) vendor=ibm ;; -os400*) vendor=ibm ;; -ptx*) vendor=sequent ;; -tpf*) vendor=ibm ;; -vxsim* | -vxworks* | -windiss*) vendor=wrs ;; -aux*) vendor=apple ;; -hms*) vendor=hitachi ;; -mpw* | -macos*) vendor=apple ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) vendor=atari ;; -vos*) vendor=stratus ;; esac basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` ;; esac echo $basic_machine$os exit # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: pgbouncer-1.7/NEWS.rst0000664000175000017500000010245412635051322011603 00000000000000PgBouncer changelog =================== PgBouncer 1.7.x --------------- **2015-12-18 - PgBouncer 1.7 - "Colors Vary After Resurrection"** - Features * Support TLS connections. OpenSSL/LibreSSL is used as backend implementation. * Support authentication via TLS client certificate. * Support "peer" authentication on Unix sockets. * Support Host Based Access control file, like `pg_hba.conf `_ in Postgres. This allows to configure TLS for network connections and "peer" authentication for local connections. - Cleanups * Set `query_wait_timeout` to 120s by default. Current default (0) causes infinite queueing, which is not useful. That means if client has pending query and has not been assigned to server connection, the client connection will be dropped. * Disable `server_reset_query_always` by default. Now reset query is used only in pools that are in session mode. * Increase pkt_buf to 4096 bytes. Improves performance with TLS. The behaviour is probably load-specific, but it should be safe to do as since v1.2 the packet buffers are split from connections and used lazily from pool. * Support pipelining count expected ReadyForQuery packets. This avoids releasing server too early. Fixes `#52 `_. * Improved sbuf_loopcnt logic - socket is guarateed to be reprocessed even if there are no event from socket. Required for TLS as it has it's own buffering. * Adapt system tests to work with modern BSD and MacOS. (Eric Radman) * Remove **crypt** auth. It's obsolete and not supported by PostgreSQL since 8.4. * Fix plain "--with-cares" configure option - without argument it was broken. PgBouncer 1.6.x --------------- **2015-09-03 - PgBouncer 1.6.1 - "Studio Audience Approves"** - Features * New setting: `server_reset_query_always`. When set, disables `server_reset_query` use on non-session pools. PgBouncer introduces per-pool pool_mode, but session-pooling and transaction-pooling should not use same reset query. In fact, transaction-pooling should not use any reset query. It is set in 1.6.x, but will be disabled in 1.7. - Fixes * [SECURITY] Remove invalid assignment of `auth_user`. (#69) When `auth_user` is set and client asks non-existing username, client will log in as `auth_user`. Not good. `CVE-2015-6817 `_ * Skip NoticeResponce in handle_auth_response. Otherwise verbose log levels on server cause login failures. * console: Fill `auth_user` when auth_type=any. Otherwise logging can crash (#67). * Various portability fixes (OpenBSD, Solaris, OSX). **2015-08-01 - PgBouncer 1.6 - "Zombies of the future"** - Features * Load user password hash from postgres database. New parameters: auth_user user to use for connecting same db and fetching user info. Can be set per-database too. auth_query SQL query to run under auth_user. Default: "SELECT usename, passwd FROM pg_shadow WHERE usename=$1" (Cody Cutrer) * Pooling mode can be configured both per-database and per-user. (Cody Cutrer) * Per-database and per-user connection limits: max_db_connections and max_user_connections. (Cody Cutrer / Pavel Stehule) * Add DISABLE/ENABLE commands to prevent new connections. (William Grant) * New DNS backend: c-ares. Only DNS backend that supports all interesting features: /etc/hosts with refresh, SOA lookup, large replies (via TCP/EDNS+UDP), IPv6. It is the preferred backend now, and probably will be **only** backend in the future, as it's pointless to support zoo of inadequate libraries. SNAFU: c-ares versions <= 1.10 have bug which breaks CNAME-s support when IPv6 has been enabled. (Fixed upstream.) As a workaround, c-ares <= 1.10 is used IPv4-only. So PgBouncer will drop other backends only when c-ares >1.10 (still unreleased) has been out some time... * Show remote_pid in SHOW CLIENTS/SERVERS. Available for clients that connect over unix sockets and both tcp and unix socket server. In case of tcp-server, the pid is taken from cancel key. * Add separate config param (dns_nxdomain_ttl) for controlling negative dns caching. (Cody Cutrer) * Add the client host IP address and port to application_name. This is enabled by a config parameter application_name_add_host which defaults to 'off'. (Andrew Dunstan) * Config files have '%include FILENAME' directive to allow configuration to be split into several files. (Andrew Dunstan) - Cleanups * log: wrap ipv6 address with [] * log: On connect to server, show local ip and port * win32: use gnu-style for long args: --foo * Allow numbers in hostname, always try to parse with inet_pton * Fix deallocate_all() in FAQ * Fix incorrect keyword in example config file (Magnus Hagander) * Allow comments (with ';') in auth files. (Guillaume Aubert) * Fix spelling mistakes in log messages and comments. (Dmitriy Olshevskiy) - Fixes * fix launching new connections during maintenance (Cody Cutrer) * don't load auth file twice at boot (Cody Cutrer) * Proper invalidation for autodbs * ipv6: Set IPV6_V6ONLY on listen socket. * win32: Don't set SO_REUSEADDR on listen socket. * Fix IPv6 address memcpy * Fix cancellation of of waiting clients. (Mathieu Fenniak) * Small bug fix, must check calloc result (Heikki Linnakangas) * Add newline at the end of the PID file (Peter Eisentraut) * Don't allow new server connections when PAUSE was issued. (Petr Jelinek) * Fix 'bad packet' during login when header is delayed. (Michał Trojnara, Marko Kreen) * Fix errors detected by Coverty. (Euler Taveira) * Disable server_idle_timeout when server count gets below min_pool (#60) (Marko Kreen) PgBouncer 1.5.x --------------- **2015-04-09 - PgBouncer 1.5.5 - "Play Dead To Win"** - Fixes * Fix remote crash - invalid packet order causes lookup of NULL pointer. Not exploitable, just DoS. **2012-11-28 - PgBouncer 1.5.4 - "No Leaks, Potty-Training Successful"** - Fixes * DNS: Fix memory leak in getaddrinfo_a() backend. * DNS: Fix memory leak in udns backend. * DNS: Fix stats calculation. * DNS: Improve error message handling for getaddrinfo_a(). * Fix win32 compile. * Fix compiler dependency support check in configure. * Few documentation fixes. **2012-09-12 - PgBouncer 1.5.3 - "Quantum Toaster"** - Critical fix * Too long database names can lead to crash, which is remotely triggerable if autodbs are enabled. The original checks assumed all names come from config files, thus using fatal() was fine, but when autodbs are enabled - by '*' in [databases] section - the database name can come from network thus making remote shutdown possible. `CVE-2012-4575 `_ - Minor Features * max_packet_size - config parameter to tune maximum packet size that is allowed through. Default is kept same: (2G-1), but now it can be made smaller. * In case of unparseable packet header, show it in hex in log and error message. - Fixes * AntiMake: it used $(relpath) and $(abspath) to manupulate pathnames, but the result was build failure when source tree path contained symlinks. The code is now changed to work on plain strings only. * console: now SET can be used to set empty string values. * config.txt: show that all timeouts can be set in floats. This is well-hidden feature introduced in 1.4. **2012-05-29 - PgBouncer 1.5.2 - "Don't Chew, Just Swallow"** - Fixes * Due to mistake, reserve_pool_timeout was taken in microseconds, not seconds, effectively activating reserve pool immediately when pool got full. Now use it as seconds, as was intended. (Noticed by Keyur Govande) **2012-04-17 - PgBouncer 1.5.1 - "Abort, Retry, Ignore?"** - Features * Parameters to tune permissions on unix socket: unix_socket_mode=0777, unix_socket_group=''. - Fixes * Allow empty string for server-side variable - this is needed to get "application_name" properly working, as it's the only parameter that does not have server-side default. * If connect string changes, require refresh of server parameters. Previously PgBouncer continued with old parameters, which breaks in case of Postgres upgrade. * If autodb connect string changes, drop old connections. * cf_setint: Use strtol() instead atoi() to parse integer config parameters. It allows hex, octal and better error detection. * Use sigqueue() to detect union sigval existence - fixes compilation on HPUX. * Remove 'git' command from Makefile, it throws random errors in case of plain-tarball build. * Document stats_period parameter. This tunes the period for stats output. * Require Asciidoc >= 8.4, seems docs are not compatible with earlier versions anymore. * Stop trying to retry on EINTR from close(). **2012-01-05 - PgBouncer 1.5 - "Bouncing Satisified Clients Since 2007"** If you use more than 8 IPs behind one DNS name, you now need to use EDNS0 protocol to query. Only getaddrinfo_a()/getaddrinfo() and UDNS backends support it, libevent 1.x/2.x does not. To enable it for libc, add 'options edns0' to /etc/resolv.conf. GNU Make 3.81+ is required for building. - Features * Detect DNS reply changes and invalidate connections to IPs no longer present in latest reply. (Petr Jelinek) * DNS zone serial based hostname invalidation. When option dns_zone_check_period is set, all DNS zones will be queried for SOA, and when serial has changed, all hostnames will be queried. This is needed to get deterministic connection invalidation, because invalidation on lookup is useless when no lookups are performed. Works only with new UDNS backend. * New SHOW DNS_HOSTS, SHOW DNS_ZONES commands to examine DNS cache. * New param: min_pool_size - avoids dropping all connections when there is no load. (Filip Rembiałkowski) * idle_in_transaction_timeout - kill transaction if idle too long. Not set by default. * New libudns backend for DNS lookups. More featureful than evdns. Use --with-udns to activate. Does not work with IPv6 yet. * KILL command, to immediately kill all connections for one database. (Michael Tharp) * Move to Antimake build system to have better looking Makefiles. Now GNU Make 3.81+ is required for building. - Fixes * DNS now works with IPv6 hostnames. * Don't change connection state when NOTIFY arrives from server. * Various documentation fixes. (Dan McGee) * Console: Support ident quoting with "". Originally we did not have any commands that took database names, so no quoting was needed. * Console: allow numbers at the stard of word regex. Trying to use strict parser makes things too complex here. * Don't expire auto DBs that are paused. (Michael Tharp) * Create auto databases as needed when doing PAUSE. (Michael Tharp) * Fix wrong log message issued by RESUME command. (Peter Eisentraut) * When user= without password= is in database connect string, password will be taken from userlist. * Parse '*' properly in takeover code. * autogen.sh: work with older autoconf/automake. * Fix run-as-service crash on win32 due to bad basename() from mingw/msvc runtime. Now compat basename() is always used. PgBouncer 1.4.x --------------- **2011-06-16 - PgBouncer 1.4.2 - "Strike-First Algorithm"** Affected OS-es: \*BSD, Solaris, Win32. - Portability Fixes * Give CFLAGS to linker. Needed when using pthread-based getaddrinfo_a() fallback. * lib/find_modules.sh: Replace split() with index()+substr(). This should make it work with older AWKs. * : Ignore system htoX/Xtoh defines. There may be only subset of macros defined. * : Separate compat sigval from compat sigevent * : Include to get iovec * : Better function autodetection on win32 * : Remove duplicate sigval/sigevent declaration **2011-04-01 - PgBouncer 1.4.1 - "It Was All An Act"** - Features * Support listening/connect for IPv6 addresses. (Hannu Krosing) * Multiple listen addresses in 'listen_addr'. For each getaddrinfo() is called, so names can also be used. * console: Send PgBouncer version as 'server_version' to client. - Important Fixes * Disable getaddrinfo_a() on glibc < 2.9 as it crashes on older versions. Notable affected OS'es: RHEL/CentOS 5.x (glibc 2.5), Ubuntu 8.04 (glibc 2.7). Also Debian/lenny (glibc 2.7) which has non-crashing getaddrinfo_a() but we have no good way to detect it. Please use libevent 2.x on such OS'es, fallback getaddrinfo_a() is not meant for production systems. And read new 'DNS lookup support' section in README to see how DNS backend is picked. (Hubert Depesz Lubaczewski, Dominique Hermsdorff, David Sommerseth) * Default to --enable-evdns if libevent 2.x is used. * Turn on tcp_keepalive by default, as that's what Postgres also does. (Hubert Depesz Lubaczewski) * Set default server_reset_query to DISCARD ALL to be compatible with Postgres by default. * win32: Fix crashes with NULL unix socket addr. (Hiroshi Saito) * Fix autodb cleanup: old cleanup code was mixing up databases and pools: as soon as one empty pool was found, the database was tagged as 'idle', potentially later killing database with active users. Reported-By: Hubert Depesz Lubaczewski - Fixes * Make compat getaddrinfo_a() non-blocking, by using single parallel thread to do lookups. * Enable pthread compilation if compat getaddrinfo_a is used. * release_server missed setting ->last_lifetime_disconnect on lifetime disconnect. (Emmanuel Courreges) * win32: fix auth file on DOS line endings - load_file() did not take account of file shringage when loading. (Rich Schaaf) * : add autoconf detection for enc/dec functions so it would not create conflicts on BSD. (James Pye) * Don't crash when config file does not exist. (Lou Picciano) * Don't crash on DNS lookup failure when logging on noise level (-v -v). (Hubert Depesz Lubaczewski, Dominique Hermsdorff) * Use backticks instead of $(cmd) in find_modules.sh to make it more portable. (Lou Picciano) * Use 'awk' instead of 'sed' in find_modules.sh to make it more portable. (Giorgio Valoti) * Log active async DNS backend info on startup. * Fix --disable-evdns to mean 'no' instead 'yes'. * Mention in docs that -R requires unix_socket_dir. * Discuss server_reset_query in faq.txt. * Restore lost memset in slab allocator * Various minor portability fixes in libusual. **2011-01-11 - PgBouncer 1.4 - "Gore Code"** - Features * Async DNS lookup - instead of resolving hostnames at reload time, the names are now resolved at connect time, with configurable caching. (See dns_max_ttl parameter.) By default it uses getaddrinfo_a() (glibc) as backend, if it does not exist, then getaddrinfo_a() is emulated via blocking(!) getaddrinfo(). When --enable-evdns argument to configure, libevent's evdns is used as backend. It is not used by default, because libevent 1.3/1.4 contain buggy implementation. Only evdns in libevent 2.0 seems OK. * New config var: syslog_ident, to tune syslog name. * Proper support for `application_name` startup parameter. * Command line long options (Guillaume Lelarge) * Solaris portability fixes (Hubert Depesz Lubaczewski) * New config var: disable_pqexec. Highly-paranoid environments can disable Simple Query Protocol with that. Requires apps that use only Extended Query Protocol. * Postgres compat: if database name is empty in startup packet, use user name as database. - Fixes * DateStyle and TimeZone server params need to use exact case. * Console: send datetime, timezone and stdstr server params to client. - Internal cleanups * Use libusual library for low-level utility functions. * Remove fixed-length limit from server params. PgBouncer 1.3.x --------------- **2010-09-09 - PgBouncer 1.3.4 - "Bouncer is always right"** - Fixes * Apply fast-fail logic at connect time. So if server is failing, the clients get error when connecting. * Don't tag automatically generated databases for checking on reload time, otherwise they get killed, because they don't exist in config. * Ignore application_name parameter by default. This avoids the need for all Postgres 9.0 users to add it into ignore_startup_parameters= themselves. * Correct pg_auth quoting. '\' is not used there. * Better error reporting on console, show incoming query to user. * Support OS'es (OpenBSD) where tv_sec is not time_t. * Avoid too noisy warnings on gcc 4.5. **2010-05-10 - PgBouncer 1.3.3 - "NSFW"** - Improvements * Make listen(2) argument configurable: listen_backlog. This is useful on OS'es, where system max allowed is configurable. * Improve disconnect messages to show what username or dbname caused login to fail. - Fixes * Move fast-fail relaunch logic around. Old one was annoying in case of permanently broken databases or users, by trying to retry even if there is no clients who want to login. * Make logging functions keep old errno, otherwise pgbouncer may act funny on higher loglevels and logging problems. * Increase the size of various startup-related buffers to handle EDB more noisy startup. * Detect V2 protocol startup request and give clear reason for disconnect. **2010-03-15 - PgBouncer 1.3.2 - "Boomerang Bullet"** - Fixes * New config var 'query_wait_timeout'. If client does not get server connection in this many seconds, it will be killed. * If no server connection in pool and last connect failed, then don't put client connections on hold but send error immediately. This together with previous fix avoids unnecessary stalls if a database has gone down. * Track libevent state in sbuf.c to avoid double event_del(). Although it usually is safe, it does not seem to work 100%. Now we should always know whether it has been called or not. * Disable maintenance during SUSPEND. Otherwise with short timeouts the old bouncer could close few connections after sending them over. * Apply client_login_timeout to clients waiting for welcome packet (first server connection). Otherwise they can stay waiting infinitely, unless there is query_timeout set. * win32: Add switch -U/-P to -regservice to let user pick account to run service under. Old automatic choice between Local Service and Local System was not reliable enough. * console: Remove \0 from end of text columns. It was hard to notice, as C clients were fine with it. * Documentation improvements. (Greg Sabino Mullane) * Clarify few login-related log messages. * Change logging level for pooler-sent errors (usually on disconnect) from INFO to WARNING, as they signify problems. * Change log message for query_timeout to "query timeout". **2009-07-06 - PgBouncer 1.3.1 - "Now fully conforming to NSA monitoring requirements"** - Fixes * Fix problem with sbuf_loopcnt which could make connections hang. If query or result length is nearby of multiple of (pktlen*sbuf_loopcnt) [10k by default], it could stay waiting for more data which will not appear. * Make database reconfigure immediate. Currently old connections could be reused after SIGHUP. * Fix SHOW DATABASES which was broken due to column addition. * Console access was disabled when "auth_mode=any" as pgbouncer dropped username. Fix: if "auth_mode=any", allow any user to console as admin. * Fix bad CUSTOM_ALIGN macro. Luckily it's unused if OS already defines ALIGN macro thus seems the bug has not happened in wild. * win32: call WSAStartup() always, not only in daemon mode as config parsing wants to resolve hosts. * win32: put quotes around config filename in service cmdline to allow spaces in paths. Executable path does not seem to need it due to some win32 magic. * Add STATS to SHOW HELP text. * doc/usage.txt: the time units in console results are in microseconds, not milliseconds. **2009-02-18 - PgBouncer 1.3 - "New Ki-Smash Finishing Move"** - Features * IANA has assigned port 6432 to be official port for PgBouncer. Thus the default port number has changed to 6432. Existing individual users do not need to change, but if you distribute packages of PgBouncer, please change the package default to official port. * Dynamic database creation (David Galoyan) Now you can define database with name "*". If defined, it's connect string will be used for all undefined databases. Useful mostly for test / dev environments. * Windows support (Hiroshi Saito) PgBouncer runs on Windows 2000+ now. Command line usage stays same, except it cannot run as daemon and cannot do online reboot. To run as service, define parameter service_name in config. Then:: > pgbouncer.exe config.ini -regservice > net start SERVICE_NAME To stop and unregister:: > net stop SERVICE_NAME > pgbouncer.exe config.ini -unregservice To use Windows Event Log, event DLL needs to be registered first:: > regsrv32 pgbevent.dll Afterwards you can set "syslog = 1" in config. - Minor features * Database names in config file can now be quoted with standard SQL ident quoting, to allow non-standard characters in db names. * New tunables: 'reserve_pool_size' and 'reserve_pool_timeout'. In case there are clients in pool that have waited more that 'reserve_pool_timeout' seconds, 'reserve_pool_size' specifies the number of connections that can be added to pool. It can also set per-pool with 'reserve_pool' connection variable. * New tunable 'sbuf_loopcnt' to limit time spent on one socket. In some situations - eg SMP server, local Postgres and fast network - pgbouncer can run recv()->send() loop many times without blocking on either side. But that means other connections will stall for a long time. To make processing more fair, limit the times of doing recv()->send() one socket. If count reaches limit, just proceed processing other sockets. The processing for that socket will resume on next event loop. Thanks to Alexander Schöcke for report and testing. * crypt() authentication is now optional, as it was removed from Postgres. If OS does not provide it, pgbouncer works fine without it. * Add milliseconds to log timestamps. * Replace old MD5 implementation with more compact one. * Update ISC licence with the FSF clarification. - Fixes * In case event_del() reports failure, just proceed with cleanup. Previously pgbouncer retried it, in case the failure was due ENOMEM. But this has caused log floods with inifinite repeats, so it seems libevent does not like it. Why event_del() report failure first time is still mystery. * --enable-debug now just toggles whether debug info is stripped from binary. It no longer plays with -fomit-frame-pointer as it's dangerous. * Fix include order, as otherwise system includes could come before internal ones. Was problem for new md5.h include file. * Include COPYRIGHT file in .tgz... PgBouncer 1.2.x --------------- **2008-08-08 - PgBouncer 1.2.3 - "Carefully Selected Bytes"** - Fixes * Disable SO_ACCEPTFILTER code for BSDs which did not work. * Include example etc/userlist.txt in tgz. * Use '$(MAKE)' instead 'make' for recursion (Jørgen Austvik) * Define _GNU_SOURCE as glibc is useless otherwise. * Let the libevent 1.1 pass link test so we can later report "1.3b+ needed" * Detect stale pidfile and remove it. Thanks to Devrim GÜNDÜZ and Bjoern Metzdorf for problem reports and testing. **2008-08-06 - PgBouncer 1.2.2 - "Barf-bag Included"** - Fixes * Remove 'drop_on_error', it was a bad idea. It was added as workaround for broken plan cache behaviour in Postgres, but can cause damage in common case when some queries always return error. **2008-08-04 - PgBouncer 1.2.1 - "Waterproof"** - Features * New parameter 'drop_on_error' - if server throws error the connection will not be reused but dropped after client finished with it. This is needed to refresh plan cache. Automatic refresh does not work even in 8.3. Defaults to 1. - Fixes * SHOW SOCKETS/CLIENTS/SERVERS: Don't crash if socket has no buffer. * Fix infinite loop on SUSPEND if suspend_timeout triggers. - Minor cleanups * Use for 'struct iovec'. * Cancel shutdown (from SIGINT) on RESUME/SIGUSR2, otherwise it will trigger on next PAUSE. * Proper log message if console operation is canceled. **2008-07-29 - PgBouncer 1.2 - "Ordinary Magic Flute"** PgBouncer 1.2 now requires libevent version 1.3b or newer. Older libevent versions crash with new restart code. - Features * Command line option (-u) and config parameter (user=) to support user switching at startup. Also now pgbouncer refuses to run as root. (Jacob Coby) * More descriptive usage text (-h). (Jacob Coby) * New database option: connect_query to allow run a query on new connections before they are taken into use. (Teodor Sigaev) * New config var 'ignore_startup_parameters' to allow and ignore extra parameters in startup packet. By default only 'database' and 'user' are allowed, all others raise error. This is needed to tolerate overenthusiastic JDBC wanting to unconditionally set 'extra_float_digits=2' in startup packet. * Logging to syslog: new parameters syslog=0/1 and syslog_facility=daemon/user/local0. * Less scary online restart (-R) - Move FD loading before fork, so it logs to console and can be canceled by ^C - Keep SHUTDOWN after fork, so ^C would be safe - A connect() is attempted to unix socket to see if anyone is listening. Now -R can be used even when no previous process was running. If there is previous process, but -R is not used, startup fails. * New console commands: - SHOW TOTALS that shows stats summary (as goes to log) plus mem usage. - SHOW ACTIVE_SOCKETS - like show sockets; but filter only active ones. - Less visible features * suspend_timeout - drop stalled conns and long logins. This brings additional safety to reboot. * When remote database throws error on logging in, notify clients. * Removing a database from config and reloading works - all connections are killed and the database is removed. * Fake some parameters on console SHOW/SET commands to be more Postgres-like. That was needed to allow psycopg to connect to console. (client_encoding/default_transaction_isolation/datestyle/timezone) * Make server_lifetime=0 disconnect server connection immediately after first use. Previously "0" made PgBouncer ignore server age. As this behavior was undocumented, there should not be any users depending on it. * Internal improvements: - Packet buffers are allocated lazily and reused. This should bring huge decrease in memory usage. This also makes realistic to use big pktbuf with lot of connections. - Lot's of error handling improvements, PgBouncer should now survive OOM situations gracefully. - Use slab allocator for memory management. - Lots of code cleanups. - Fixes * Only single accept() was issued per event loop which could cause connection backlog when having high amount of connection attempts. Now the listening socket is always drained fully, which should fix this. * Handle EINTR from connect(). * Make configure.ac compatible with autoconf 2.59. * Solaris compatibility fixes (Magne Mæhre) PgBouncer 1.1.x --------------- **2007-12-10 - PgBouncer 1.1.2 - "The Hammer"** - Features * Disconnects because of server_lifetime are now separated by (server_lifetime / pool_size) seconds. This avoids pgbouncer causing reconnect floods. - Fixes * Online upgrade 1.0 -> 1.1 problems: - 1.0 does not track server parameters, so they stay NULL but 1.1 did not expect it and crashed. - If server params are unknown, but client ones are set, then issue a SET for them, instead complaining. * Remove temp debug statements that were accidentally left in code on INFO level, so they polluted logs. * Unbroke debian/changelog - Cleanup * reorder struct SBuf fields to get better alignment for buffer. **2007-10-26 - PgBouncer 1.1.1 - "Breakdancing Bee"** - Fixes * Server parameter cache could stay uninitialized, which caused unnecessary SET of them. This caused problem on 8.1 which does not allow touching standard_conforming_strings. (Thanks to Dimitri Fontaine for report & testing.) * Some doc fixes. * Include doc/fixman.py in .tgz. **2007-10-09 - PgBouncer 1.1 - "Mad-Hat Toolbox"** - Features * Keep track of following server parameters:: client_encoding datestyle, timezone, standard_conforming_strings * Database connect string enhancements: - Accept hostname in host= - Accept custom unix socket location in host= - Accept quoted values: password=' asd''foo' * New config var: server_reset_query, to be sent immidiately after release * New config var: server_round_robin, to switch between LIFO and RR. * Cancel pkt sent for idle connection does not drop it anymore. * Cancel with ^C from psql works for SUSPEND / PAUSE. * Print FD limits on startup. * When suspending, try to hit packet boundary ASAP. * Add 'timezone' to database parameters. * Use longlived logfile fd. Reopened on SIGHUP / RELOAD; * Local connection endpoint info in SHOW SERVERS/CLIENTS/SOCKETS. - Code cleanup * More debug log messages include socket info. * Magic number removal and error message cleanup. (David Fetter) * Wrapper struct for current pkt info. Removes lot of compexity. - Fixes * Detect invalid pkt headers better. * auth_file modification check was broken, which made pgbouncer reload it too often. PgBouncer 1.0.x --------------- **2007-06-18 - PgBouncer 1.0.8 - "Undead Shovel Jutsu"** - Fixes * Fix crash in cancel packet handling. (^C from psql) - Features * PAUSE ; RESUME ; works now. * Cleanup of console command parsing. * Disable expensive in-list assert check. **2007-04-19 - PgBouncer 1.0.7 - "With Vitamin A-Z"** - Fixes * Several error/notice packets with send() blocking between triggered assert. Fix it by removing flushing logic altogether. As pgbouncer does not actively buffer anything, its not needed. It was a remnant from the time when buffering was pushed to kernel with MSG_MORE. * Additionally avoid calling recv() logic when sending unblocks. * List search code for admin_users and stats_users mishandled partial finds. Fix it. * Standardise UNIX socket peer UID finding to getpeereid(). **2007-04-12 - PgBouncer 1.0.6 - "Daily Dose"** - Fixes * The "Disable maintenance during the takeover" fix could disable maintenance altogether. Fix it. * Compilation fix for FreeBSD, requires there. Thanks go to Robert Gogolok for report. **2007-04-11 - PgBouncer 1.0.5 - "Enough for today"** - Fixes * Fix online-restart bugs: - Set ->ready for idle servers. - Remove obsolete code from use_client_socket() - Disable maintenance during the takeover. **2007-04-11 - PgBouncer 1.0.4 - "Last 'last' bug"** - Fixes * Notice from idle server tagged server dirty. release_server() did not expect it. Fix it by dropping them. **2007-04-11 - PgBouncer 1.0.3 - "Fearless Fork"** - Fixes * Some error handling was missing in login path, so dying connection there could trigger asserts. * Cleanup of asserts in sbuf.c to catch problems earlier. * Create core when Assert() triggers. - New stuff * New config vars: log_connections, log_disconnections, log_pooler_errors to turn on/off noise. * Config var: client_login_timeout to kill dead connections in login phase that could stall SUSPEND and thus online restart. **2007-03-28 - PgBouncer 1.0.2 - "Supersonic Spoon"** - Fixes * libevent may report a deleted event inside same loop. Avoid socket reuse for one loop. * release_server() from disconnect_client() didnt look it the packet was actually sent. **2007-03-15 - PgBouncer 1.0.1 - "Alien technology"** - Fixes * Mixed usage of cached and non-cached time, plus unsiged usec_t typedef created spurious query_timeout errors. * Fix rare case when socket woken up from send-wait could stay stalling. * More fair queueing of server connections. Before, a new query could get a server connections before older one. * Delay server release until everything is guaranteed to be sent. - Features * SHOW SOCKETS command to have detailed info about state state. * Put PgSocket ptr to log, to help tracking one connection. * In console, allow SELECT in place of SHOW. * Various code cleanups. **2007-03-13 - PgBouncer 1.0 - "Tuunitud bemm"** - First public release. pgbouncer-1.7/lib/0000775000175000017500000000000012635051616011123 500000000000000pgbouncer-1.7/lib/m4/0000775000175000017500000000000012635051616011443 500000000000000pgbouncer-1.7/lib/m4/usual.m40000664000175000017500000003327712615704017012770 00000000000000 dnl Those depend on correct order: dnl AC_USUAL_INIT dnl AC_USUAL_PROGRAM_CHECK dnl AC_USUAL_HEADER_CHECK dnl AC_USUAL_TYPE_CHECK dnl AC_USUAL_FUNCTION_CHECK dnl Order does not matter: dnl AC_USUAL_CASSERT dnl AC_USUAL_WERROR dnl AC_USUAL_DEBUG dnl Optional features: dnl AC_USUAL_LIBEVENT / AC_USUAL_LIBEVENT_OPT dnl AC_USUAL_UREGEX dnl AC_USUAL_GETADDRINFO_A dnl AC_USUAL_TLS dnl dnl AC_USUAL_INIT: dnl - Sets PORTNAME=win32/unix dnl - If building from separate dir, writes top-level Makefile (antimake) dnl dnl Also defines port-specific flags: dnl _GNU_SOURCE, _WIN32_WINNT, WIN32_LEAN_AND_MEAN dnl AC_DEFUN([AC_USUAL_INIT], [ # if building separately from srcdir, write top-level makefile if test "$srcdir" != "."; then echo "include $srcdir/Makefile" > Makefile fi AC_MSG_CHECKING([target host type]) xhost="$host_alias" if test "x$xhost" = "x"; then xhost=`uname -s` fi case "$xhost" in *cygwin* | *mingw* | *pw32* | *MINGW*) LIBS="$LIBS -lws2_32" PORTNAME=win32;; *) PORTNAME=unix ;; esac AC_SUBST(PORTNAME) AC_MSG_RESULT([$PORTNAME]) dnl Set the flags before any feature tests. if test "$PORTNAME" = "win32"; then AC_DEFINE([WIN32_LEAN_AND_MEAN], [1], [Define to request cleaner win32 headers.]) AC_DEFINE([WINVER], [0x0501], [Define to max win32 API version (0x0501=XP).]) else AC_DEFINE([_GNU_SOURCE], [1], [Define to get working glibc.]) fi dnl Package-specific data AC_SUBST([pkgdatadir], ['${datarootdir}'/${PACKAGE_TARNAME}]) dnl pkgconfig files AC_SUBST([pkgconfigdir], ['${libdir}/pkgconfig']) ]) dnl Old name for initial checks AC_DEFUN([AC_USUAL_PORT_CHECK], [AC_USUAL_INIT]) dnl dnl AC_USUAL_PROGRAM_CHECK: Simple C environment: CC, CPP, INSTALL dnl AC_DEFUN([AC_USUAL_PROGRAM_CHECK], [ AC_PROG_CC_STDC AC_PROG_CPP dnl Check if compiler supports __func__ AC_CACHE_CHECK([whether compiler supports __func__], pgac_cv_funcname_func, [AC_TRY_COMPILE([#include ], [printf("%s\n", __func__);], [pgac_cv_funcname_func=yes], [pgac_cv_funcname_func=no])]) if test x"$pgac_cv_funcname_func" = xyes ; then AC_DEFINE(HAVE_FUNCNAME__FUNC, 1, [Define to 1 if your compiler understands __func__.]) fi dnl Check if linker supports -Wl,--as-needed if test "$GCC" = "yes"; then old_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS -Wl,--as-needed" AC_MSG_CHECKING([whether linker supports --as-needed]) AC_LINK_IFELSE([AC_LANG_SOURCE([int main(void) { return 0; }])], [AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no]) LDFLAGS="$old_LDFLAGS"]) fi dnl Check if compiler supports gcc-style dependencies AC_MSG_CHECKING([whether compiler supports dependency generation]) old_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -MD -MP -MT conftest.o -MF conftest.o.d" AC_COMPILE_IFELSE([AC_LANG_SOURCE([void foo(void){}])], [HAVE_CC_DEPFLAG=yes], [HAVE_CC_DEPFLAG=no]) rm -f conftest.d CFLAGS="$old_CFLAGS" AC_MSG_RESULT([$HAVE_CC_DEPFLAG]) AC_SUBST(HAVE_CC_DEPFLAG) dnl Pick good warning flags for gcc WFLAGS="" if test x"$GCC" = xyes; then AC_MSG_CHECKING([for working warning switches]) good_CFLAGS="$CFLAGS" flags="-Wall -Wextra" # turn off noise from Wextra flags="$flags -Wno-unused-parameter -Wno-missing-field-initializers" # Wextra does not turn those on? flags="$flags -Wmissing-prototypes -Wpointer-arith -Wendif-labels" flags="$flags -Wdeclaration-after-statement -Wold-style-definition" flags="$flags -Wstrict-prototypes -Wundef -Wformat=2" flags="$flags -Wuninitialized" for f in $flags; do CFLAGS="$good_CFLAGS $WFLAGS $f" AC_COMPILE_IFELSE([AC_LANG_SOURCE([void foo(void){}])], [WFLAGS="$WFLAGS $f"]) done # avoid -Wextra if missing-field.initializers does not work echo "$WFLAGS" | grep missing-field-initializers > /dev/null \ || WFLAGS=`echo "$WFLAGS"|sed 's/ -Wextra//'` CFLAGS="$good_CFLAGS" AC_MSG_RESULT([done]) fi AC_SUBST(WFLAGS) AC_PROG_INSTALL AC_PROG_LN_S AC_PROG_EGREP AC_PROG_AWK dnl AC_PROG_MKDIR_P and AC_PROG_SED are from newer autotools m4_ifdef([AC_PROG_MKDIR_P], [ AC_PROG_MKDIR_P ], [ MKDIR_P="mkdir -p" AC_SUBST(MKDIR_P) ]) m4_ifdef([AC_PROG_SED], [ AC_PROG_SED ], [ SED="sed" AC_SUBST(SED) ]) dnl Convert relative path to absolute path. case "$ac_install_sh" in ./*) ac_install_sh="`pwd`/${ac_install_sh}" ;; ../*) ac_install_sh="`pwd`/${ac_install_sh}" ;; esac case "$INSTALL" in ./*) INSTALL="`pwd`/${INSTALL}" ;; ../*) INSTALL="`pwd`/${INSTALL}" ;; esac case "$MKDIR_P" in ./*) MKDIR_P="`pwd`/${MKDIR_P}" ;; ../*) MKDIR_P="`pwd`/${MKDIR_P}" ;; esac AC_CHECK_TOOL([STRIP], [strip]) AC_CHECK_TOOL([RANLIB], [ranlib], [true]) AC_CHECK_TOOL([AR], [ar]) ARFLAGS=rcu AC_SUBST(ARFLAGS) ]) dnl dnl AC_USUAL_TYPE_CHECK: Basic types for C dnl AC_DEFUN([AC_USUAL_TYPE_CHECK], [ AC_C_INLINE AC_C_RESTRICT AC_C_BIGENDIAN AC_SYS_LARGEFILE AC_TYPE_PID_T AC_TYPE_UID_T AC_TYPE_SIZE_T ]) dnl dnl AC_USUAL_HEADER_CHECK: Basic headers dnl AC_DEFUN([AC_USUAL_HEADER_CHECK], [ AC_CHECK_HEADERS([inttypes.h stdbool.h unistd.h sys/time.h]) AC_CHECK_HEADERS([sys/socket.h poll.h sys/poll.h sys/un.h]) AC_CHECK_HEADERS([arpa/inet.h netinet/in.h netinet/tcp.h]) AC_CHECK_HEADERS([sys/param.h sys/uio.h pwd.h grp.h]) AC_CHECK_HEADERS([sys/wait.h sys/mman.h syslog.h netdb.h dlfcn.h]) AC_CHECK_HEADERS([err.h pthread.h endian.h sys/endian.h byteswap.h]) AC_CHECK_HEADERS([malloc.h regex.h getopt.h fnmatch.h]) AC_CHECK_HEADERS([langinfo.h xlocale.h]) dnl ucred.h may have prereqs AC_CHECK_HEADERS([ucred.h sys/ucred.h], [], [], [ #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_PARAM_H #include #endif ]) ]) dnl dnl AC_USUAL_FUNCTION_CHECK: Basic functions dnl AC_DEFUN([AC_USUAL_FUNCTION_CHECK], [ ### Functions provided if missing dnl AC_CHECK_FUNCS(basename dirname) # unstable, provide always AC_CHECK_FUNCS(strlcpy strlcat strsep getpeereid sigaction sigqueue) AC_CHECK_FUNCS(memmem memrchr mempcpy) AC_CHECK_FUNCS(inet_ntop inet_pton poll getline regcomp) AC_CHECK_FUNCS(err errx warn warnx getprogname setprogname) AC_CHECK_FUNCS(posix_memalign memalign valloc explicit_bzero memset_s reallocarray) AC_CHECK_FUNCS(getopt getopt_long getopt_long_only) AC_CHECK_FUNCS(fls flsl flsll ffs ffsl ffsll) AC_CHECK_FUNCS(fnmatch mbsnrtowcs nl_langinfo strtod_l strtonum) AC_CHECK_FUNCS(asprintf vasprintf timegm) ### Functions provided only on win32 AC_CHECK_FUNCS(localtime_r gettimeofday recvmsg sendmsg usleep getrusage) ### Functions used by libusual itself AC_CHECK_FUNCS(syslog mmap getpeerucred arc4random_buf getentropy) ### win32: link with ws2_32 AC_SEARCH_LIBS(WSAGetLastError, ws2_32) AC_FUNC_STRERROR_R ### AC_MSG_CHECKING([for integer enc/dec functions]) AC_LINK_IFELSE([AC_LANG_SOURCE([ #include #ifdef HAVE_SYS_ENDIAN_H #include #endif #ifdef HAVE_ENDIAN_H #include #endif char p[[]] = "01234567"; int main(void) { be16enc(p, 0); be32enc(p, 1); be64enc(p, 2); le16enc(p, 2); le32enc(p, 3); le64enc(p, 4); return (int)(be16dec(p) + be32dec(p) + be64dec(p)) + (int)(le16dec(p) + le32dec(p) + le64dec(p)); } ])], [ AC_MSG_RESULT([found]) AC_DEFINE([HAVE_ENCDEC_FUNCS], [1], [Define if *enc & *dec functions are available]) ], [AC_MSG_RESULT([not found])]) ]) dnl dnl AC_USUAL_CASSERT: --enable-cassert switch to set macro CASSERT dnl AC_DEFUN([AC_USUAL_CASSERT], [ AC_ARG_ENABLE(cassert, AC_HELP_STRING([--enable-cassert],[turn on assert checking in code])) AC_MSG_CHECKING([whether to enable asserts]) if test "$enable_cassert" = "yes"; then AC_DEFINE(CASSERT, 1, [Define to enable assert checking]) AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi ]) dnl dnl AC_USUAL_WERROR: --enable-werror switch to turn warnings into errors dnl AC_DEFUN([AC_USUAL_WERROR], [ AC_ARG_ENABLE(werror, AC_HELP_STRING([--enable-werror],[add -Werror to CFLAGS])) AC_MSG_CHECKING([whether to fail on warnings]) if test "$enable_werror" = "yes"; then CFLAGS="$CFLAGS -Werror" AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi ]) dnl dnl AC_USUAL_DEBUG: --disable-debug switch to strip binary dnl AC_DEFUN([AC_USUAL_DEBUG], [ AC_ARG_ENABLE(debug, AC_HELP_STRING([--disable-debug],[strip binary]), [], [enable_debug=yes]) AC_MSG_CHECKING([whether to build debug binary]) if test "$enable_debug" = "yes"; then LDFLAGS="-g $LDFLAGS" BININSTALL="$INSTALL" AC_MSG_RESULT([yes]) else BININSTALL="$INSTALL -s" AC_MSG_RESULT([no]) fi AC_SUBST(enable_debug) ]) dnl dnl AC_USUAL_LIBEVENT: --with-libevent dnl dnl AC_USUAL_LIBEVENT - prefer-yes: dnl default - search for libevent, error if not found dnl --with - search for libevent, error if not found dnl --without - use libusual dnl dnl AC_USUAL_LIBEVENT_OPT - prefer-no: dnl default - use libusual dnl --with - search for libevent, error if not found dnl --without - use libusual dnl AC_DEFUN([AC_USUAL_LIBEVENT_OPT], [AC_USUAL_LIBEVENT(1)]) AC_DEFUN([AC_USUAL_LIBEVENT], [ ifelse([$#], [0], [levent=yes], [levent=no]) AC_MSG_CHECKING([for libevent]) AC_ARG_WITH(libevent, AC_HELP_STRING([--with-libevent=prefix],[Specify where libevent is installed]), [ if test "$withval" = "no"; then levent=no elif test "$withval" = "yes"; then levent=yes else levent=yes CPPFLAGS="$CPPFLAGS -I$withval/include" LDFLAGS="$LDFLAGS -L$withval/lib" fi ], []) if test "$levent" = "no"; then AC_MSG_RESULT([using usual/event]) AC_DEFINE(HAVE_EVENT_LOOPBREAK, 1, [usual/event.h has it.]) AC_DEFINE(HAVE_EVENT_BASE_NEW, 1, [usual/event.h has it.]) have_libevent=no else # libevent AC_DEFINE(HAVE_LIBEVENT, 1, [Use real libevent.]) LIBS="-levent $LIBS" AC_LINK_IFELSE([AC_LANG_SOURCE([ #include #include #include #include int main(void) { struct event ev; event_init(); event_set(&ev, 1, EV_READ, NULL, NULL); /* this checks for 1.2+ */ event_base_free(NULL); } ])], [AC_MSG_RESULT([found])], [AC_MSG_ERROR([not found, cannot proceed])]) AC_CHECK_FUNCS(event_loopbreak event_base_new evdns_base_new) have_libevent=yes fi # libevent AC_SUBST(have_libevent) ]) dnl AC_USUAL_LIBEVENT dnl dnl AC_USUAL_UREGEX: --with-uregex dnl dnl Allow override of system regex dnl AC_DEFUN([AC_USUAL_UREGEX], [ AC_MSG_CHECKING([whether to force internal regex]) uregex=no AC_ARG_WITH(uregex, AC_HELP_STRING([--with-uregex],[Force use of internal regex]), [ if test "$withval" = "yes"; then uregex=yes fi ], []) if test "$uregex" = "yes"; then AC_MSG_RESULT([yes]) AC_DEFINE(USE_INTERNAL_REGEX, 1, [Define to force use of uRegex.]) else AC_MSG_RESULT([no]) fi ]) dnl AC_USUAL_UREGEX dnl dnl AC_USUAL_GETADDRINFO_A - getaddrinfo_a() is required dnl AC_DEFUN([AC_USUAL_GETADDRINFO_A], [ AC_SEARCH_LIBS(getaddrinfo_a, anl) AC_CACHE_CHECK([whether to use native getaddinfo_a], ac_cv_usual_glibc_gaia, [AC_TRY_LINK([ #include #ifdef HAVE_NETDB_H #include #endif ], [ #if __GLIBC_PREREQ(2,9) getaddrinfo_a(0,NULL,0,NULL); #else none or broken #endif ], [ac_cv_usual_glibc_gaia=yes], [ac_cv_usual_glibc_gaia=no])]) if test x"$ac_cv_usual_glibc_gaia" = xyes ; then AC_DEFINE(HAVE_GETADDRINFO_A, 1, [Define to 1 if you have the getaddrinfo_a() function.]) else ACX_PTHREAD(, [AC_MSG_RESULT([Threads not available and fallback getaddrinfo_a() non-functional.])]) CC="$PTHREAD_CC" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LIBS="$LIBS $PTHREAD_LIBS" fi ]) dnl dnl AC_USUAL_TLS: --with-openssl [ / --with-gnutls ? ] dnl dnl AC_USUAL_TLS - prefer-yes: dnl default - search for libssl, error if not found dnl --with-openssl - search for libssl, error if not found dnl --with-openssl=pfx - search for libssl, error if not found, use pfx dnl --without-openssl - no tls dnl AC_DEFUN([AC_USUAL_TLS],[ dnl values: no, libssl, auto tls_support=auto TLS_CPPFLAGS="" TLS_LDFLAGS="" TLS_LIBS="" AC_MSG_CHECKING([for OpenSSL]) AC_ARG_WITH(openssl, AC_HELP_STRING([--with-openssl=prefix], [Specify where OpenSSL is installed]), [ if test "$withval" = "no"; then tls_support=no elif test "$withval" = "yes"; then tls_support=libssl TLS_LIBS="-lssl -lcrypto" else tls_support=libssl TLS_CPPFLAGS="-I$withval/include" TLS_LDFLAGS="-L$withval/lib" TLS_LIBS="-lssl -lcrypto" fi ], [ tls_support=auto TLS_CPPFLAGS="" TLS_LDFLAGS="" TLS_LIBS="-lssl -lcrypto" ]) dnl check if libssl works if test "$tls_support" = "auto" -o "$tls_support" = "libssl"; then AC_DEFINE(USUAL_LIBSSL_FOR_TLS, 1, [Use libssl for TLS.]) tmp_LIBS="$LIBS" tmp_LDFLAGS="$LDFLAGS" tmp_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$TLS_CPPFLAGS $CPPFLAGS" LDFLAGS="$TLS_LDFLAGS $LDFLAGS" LIBS="$TLS_LIBS $LIBS" AC_LINK_IFELSE([ AC_LANG_PROGRAM([[#include ]], [[SSL_CTX *ctx = SSL_CTX_new(TLSv1_2_method());]])], [ tls_support=yes; AC_MSG_RESULT([found])], [ AC_MSG_ERROR([not found]) ]) dnl check LibreSSL-only APIs AC_CHECK_FUNCS(SSL_CTX_use_certificate_chain_mem SSL_CTX_load_verify_mem asn1_time_parse) CPPFLAGS="$tmp_CPPFLAGS" LDFLAGS="$tmp_LDFLAGS" LIBS="$tmp_LIBS" dnl Pick default root CA file cafile=auto AC_MSG_CHECKING([for root CA certs]) AC_ARG_WITH(root-ca-file, AC_HELP_STRING([--with-root-ca-file=cafile], [Specify where root CA certs are.]), [ if test "$withval" = "no"; then : elif test "$withval" = "yes"; then : else cafile="$withval" fi ]) if test "$cafile" = "auto"; then for cafile in /etc/ssl/certs/ca-certificates.crt /etc/ssl/cert.pem; do if test -f "$cafile"; then break fi done fi AC_DEFINE_UNQUOTED(USUAL_TLS_CA_FILE, ["$cafile"], [Path to root CA certs.]) AC_MSG_RESULT([$cafile]) else AC_MSG_RESULT([no]) fi AC_SUBST(tls_support) AC_SUBST(TLS_CPPFLAGS) AC_SUBST(TLS_LDFLAGS) AC_SUBST(TLS_LIBS) ]) dnl AC_USUAL_TLS pgbouncer-1.7/lib/m4/acx_pthread.m40000664000175000017500000001613112511202014014070 00000000000000AC_DEFUN([ACX_PTHREAD], [ AC_REQUIRE([AC_CANONICAL_HOST]) AC_LANG_SAVE AC_LANG_C acx_pthread_ok=no # We used to check for pthread.h first, but this fails if pthread.h # requires special compiler flags (e.g. on True64 or Sequent). # It gets checked for in the link test anyway. # First of all, check if the user has set any of the PTHREAD_LIBS, # etcetera environment variables, and if threads linking works using # them: if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes) AC_MSG_RESULT($acx_pthread_ok) if test x"$acx_pthread_ok" = xno; then PTHREAD_LIBS="" PTHREAD_CFLAGS="" fi LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" fi # We must check for the threads library under a number of different # names; the ordering is very important because some systems # (e.g. DEC) have both -lpthread and -lpthreads, where one of the # libraries is broken (non-POSIX). # Create a list of thread flags to try. Items starting with a "-" are # C compiler flags, and other items are library names, except for "none" # which indicates that we try without any flags at all, and "pthread-config" # which is a program returning the flags for the Pth emulation library. acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" # The ordering *is* (sometimes) important. Some notes on the # individual items follow: # pthreads: AIX (must check this before -lpthread) # none: in case threads are in libc; should be tried before -Kthread and # other compiler flags to prevent continual compiler warnings # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) # -pthreads: Solaris/gcc # -mthreads: Mingw32/gcc, Lynx/gcc # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it # doesn't hurt to check since this sometimes defines pthreads too; # also defines -D_REENTRANT) # ... -mt is also the pthreads flag for HP/aCC # pthread: Linux, etcetera # --thread-safe: KAI C++ # pthread-config: use pthread-config program (for GNU Pth library) case "${host_cpu}-${host_os}" in *solaris*) # On Solaris (at least, for some versions), libc contains stubbed # (non-functional) versions of the pthreads routines, so link-based # tests will erroneously succeed. (We need to link with -pthreads/-mt/ # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather # a function called by this macro, so we could check for that, but # who knows whether they'll stub that too in a future libc.) So, # we'll just look for -pthreads and -lpthread first: acx_pthread_flags="-pthreads pthread -mt -pthread $acx_pthread_flags" ;; esac if test x"$acx_pthread_ok" = xno; then for flag in $acx_pthread_flags; do case $flag in none) AC_MSG_CHECKING([whether pthreads work without any flags]) ;; -*) AC_MSG_CHECKING([whether pthreads work with $flag]) PTHREAD_CFLAGS="$flag" ;; pthread-config) AC_CHECK_PROG(acx_pthread_config, pthread-config, yes, no) if test x"$acx_pthread_config" = xno; then continue; fi PTHREAD_CFLAGS="`pthread-config --cflags`" PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" ;; *) AC_MSG_CHECKING([for the pthreads library -l$flag]) PTHREAD_LIBS="-l$flag" ;; esac save_LIBS="$LIBS" save_CFLAGS="$CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # Check for various functions. We must include pthread.h, # since some functions may be macros. (On the Sequent, we # need a special flag -Kthread to make this header compile.) # We check for pthread_join because it is in -lpthread on IRIX # while pthread_create is in libc. We check for pthread_attr_init # due to DEC craziness with -lpthreads. We check for # pthread_cleanup_push because it is one of the few pthread # functions on Solaris that doesn't have a non-functional libc stub. # We try pthread_create on general principles. AC_TRY_LINK([#include <pthread.h>], [pthread_t th; pthread_join(th, 0); pthread_attr_init(0); pthread_cleanup_push(0, 0); pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], [acx_pthread_ok=yes]) LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" AC_MSG_RESULT($acx_pthread_ok) if test "x$acx_pthread_ok" = xyes; then break; fi PTHREAD_LIBS="" PTHREAD_CFLAGS="" done fi # Various other checks: if test "x$acx_pthread_ok" = xyes; then save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. AC_MSG_CHECKING([for joinable pthread attribute]) attr_name=unknown for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do AC_TRY_LINK([#include <pthread.h>], [int attr=$attr; return attr;], [attr_name=$attr; break]) done AC_MSG_RESULT($attr_name) if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name, [Define to necessary symbol if this constant uses a non-standard name on your system.]) fi AC_MSG_CHECKING([if more special flags are required for pthreads]) flag=no case "${host_cpu}-${host_os}" in *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";; *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";; esac AC_MSG_RESULT(${flag}) if test "x$flag" != xno; then PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" fi LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" # More AIX lossage: must compile with xlc_r or cc_r if test x"$GCC" != xyes; then AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC}) else PTHREAD_CC=$CC fi else PTHREAD_CC="$CC" fi AC_SUBST(PTHREAD_LIBS) AC_SUBST(PTHREAD_CFLAGS) AC_SUBST(PTHREAD_CC) # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: if test x"$acx_pthread_ok" = xyes; then ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) : else acx_pthread_ok=no $2 fi AC_LANG_RESTORE ])dnl ACX_PTHREAD pgbouncer-1.7/lib/m4/antimake.m40000664000175000017500000000033612511202014013377 00000000000000 dnl dnl AMK_INIT: Generate initial makefile dnl AC_DEFUN([AMK_INIT], [ # if building separately from srcdir, write top-level makefile if test "$srcdir" != "."; then echo "include $srcdir/Makefile" > Makefile fi ]) pgbouncer-1.7/lib/find_modules.sh0000775000175000017500000000167112511203511014041 00000000000000#! /bin/sh set -e top="$1" # sanity check test -n "$top" || { echo "usage: $0 USUAL_DIR SRC ..." >&2 exit 1 } test -f "$top/usual/base.h" || { echo "usage: $0 USUAL_DIR SRC ..." >&2 exit 1 } shift test -n "$1" || exit 0 test -n "$AWK" || AWK=awk # return uniq module names, exclude already found ones grep_usual() { excl='excl["config"]=1' for m in $m_done; do excl="$excl;excl[\"$m\"]=1" done prog=' BEGIN { '"$excl"' } /^#include[ \t]*[<"]usual\// { p1 = index($0, "/"); p2 = index($0, "."); m = substr($0, p1+1, p2-p1-1); if (!excl[m]) print m; }' $AWK "$prog" "$@" | sort -u } # return module filename globs make_pats() { for m in "$@"; do echo "$top/usual/$m*.[ch]" done } # loop over grep until all mods are found m_done="" m_tocheck=`grep_usual "$@"` while test -n "$m_tocheck"; do m_done="$m_done $m_tocheck" pats=`make_pats $m_tocheck` m_tocheck=`grep_usual $pats` done # done echo $m_done pgbouncer-1.7/lib/COPYRIGHT0000664000175000017500000000151212511202014012314 00000000000000/* * libusual - Utility library for C * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ pgbouncer-1.7/lib/README0000664000175000017500000000011212511202014011674 00000000000000= libusual = Collection of various code useful for writing server code. pgbouncer-1.7/lib/usual/0000775000175000017500000000000012635051616012254 500000000000000pgbouncer-1.7/lib/usual/pgutil.c0000664000175000017500000001141712511202014013627 00000000000000/* * Some utility functions for Postgres. * * - Literal & ident quoting. * - Array parsing */ #include #include /* str -> E'str' */ bool pg_quote_literal(char *_dst, const char *_src, int dstlen) { char *dst = _dst; char *end = _dst + dstlen - 2; const char *src = _src; bool stdquote = true; if (dstlen < 3) return false; if (_src == NULL) { if (dstlen < 5) return false; memcpy(_dst, "NULL", 5); return true; } retry: *dst++ = '\''; while (*src && dst < end) { if (*src == '\'') *dst++ = '\''; else if (*src == '\\') { if (stdquote) goto retry_ext; *dst++ = '\\'; } *dst++ = *src++; } if (*src || dst > end) return false; *dst++ = '\''; *dst = 0; return true; retry_ext: /* string contains '\\', retry as E'' string */ dst = _dst; src = _src; *dst++ = 'E'; stdquote = false; goto retry; } static inline bool id_start(unsigned char c) { return (c >= 'a' && c <= 'z') || c == '_'; } static inline bool id_body(unsigned char c) { return id_start(c) || (c >= '0' && c <= '9'); } /* ident -> "ident" */ bool pg_quote_ident(char *_dst, const char *_src, int dstlen) { char *dst = _dst; char *end = _dst + dstlen - 1; const char *src = _src; if (dstlen < 1) return false; if (!id_start(*src)) goto needs_quoting; while (*src && dst < end) { if (!id_body(*src)) goto needs_quoting; *dst++ = *src++; } if (*src) return false; *dst = 0; if (!pg_is_reserved_word(_dst)) return true; needs_quoting: dst = _dst; src = _src; end = _dst + dstlen - 2; if (dstlen < 3) return false; *dst++ = '"'; while (*src && dst < end) { if (*src == '"') *dst++ = *src; *dst++ = *src++; } if (*src) return false; *dst++ = '"'; *dst = 0; return true; } /* schema.name -> "schema"."name" */ bool pg_quote_fqident(char *_dst, const char *_src, int dstlen) { const char *dot = strchr(_src, '.'); char scmbuf[128]; const char *scm; int scmlen; if (dot) { scmlen = dot - _src; if (scmlen >= (int)sizeof(scmbuf)) return false; memcpy(scmbuf, _src, scmlen); scmbuf[scmlen] = 0; scm = scmbuf; _src = dot + 1; } else { scm = "public"; } if (!pg_quote_ident(_dst, scm, dstlen)) return false; scmlen = strlen(_dst); _dst[scmlen] = '.'; _dst += scmlen + 1; dstlen -= scmlen + 1; if (!pg_quote_ident(_dst, _src, dstlen)) return false; return true; } /* * pgarray parsing */ static bool parse_value(struct StrList *arr, const char *val, const char *vend, CxMem *cx) { int len; const char *s; char *str, *p; unsigned c; while (val < vend && isspace(*val)) val++; while (vend > val && isspace(vend[-1])) vend--; if (val == vend) return false; s = val; len = vend - val; if (len == 4 && !strncasecmp(val, "null", len)) { return strlist_append_ref(arr, NULL); } p = str = cx_alloc(cx, len + 1); if (!str) return false; /* unquote & copy */ while (s < vend) { c = *s++; if (c == '"') { while (1) { c = *s++; if (c == '"') break; else if (c == '\\') *p++ = *s++; else *p++ = c; } } else if (c == '\\') { *p++ = *s++; } else *p++ = c; } *p++ = 0; if (!strlist_append_ref(arr, str)) { cx_free(cx, str); return false; } return true; } struct StrList *pg_parse_array(const char *pgarr, CxMem *cx) { const char *s = pgarr; struct StrList *lst; const char *val = NULL; unsigned c; /* skip dimension def "[x,y]={..}" */ if (*s == '[') { s = strchr(s, ']'); if (!s || s[1] != '=') return NULL; s += 2; } if (*s++ != '{') return NULL; lst = strlist_new(cx); if (!lst) return NULL; while (*s) { /* array end */ if (s[0] == '}') { if (s[1] != 0) { goto failed; } if (val) { if (!parse_value(lst, val, s, cx)) goto failed; } return lst; } /* cannot init earlier to support empty arrays */ if (!val) val = s; /* val done? */ if (*s == ',') { if (!parse_value(lst, val, s, cx)) goto failed; val = ++s; continue; } /* scan value */ c = *s++; if (c == '"') { while (1) { c = *s++; if (c == '"') break; else if (c == '\\') { if (!*s) goto failed; s++; } else if (!*s) goto failed; } } else if (c == '\\') { if (!*s) goto failed; s++; } } if (s[-1] != '}') goto failed; return lst; failed: strlist_free(lst); return NULL; } /* * Postgres keyword lookup. */ /* gperf tries ot inline a non-static function. */ #undef inline #undef __inline #undef __attribute__ #define inline #define __inline #define __attribute__(x) #define long uintptr_t /* include gperf code */ const char *pg_keyword_lookup_real(const char *str, unsigned int len); #include bool pg_is_reserved_word(const char *str) { const char *kw = pg_keyword_lookup_real(str, strlen(str)); return kw != NULL; } pgbouncer-1.7/lib/usual/psrandom.h0000664000175000017500000000347512567562476014220 00000000000000/* * Pseudo-random bytes. * * Copyright (c) 2015 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * Pseudo-random number generator for non-cryptographic purposes. * * By default it's seeded with csrandom so returns unpredictable * values (but not cryptographically stong). But when * pseudo_random_seed() is used, all following values * are determined completely by seed. */ #ifndef _USUAL_PSRANDOM_H_ #define _USUAL_PSRANDOM_H_ #include /** * Return value with uniform probability over whole 32-bit range. */ uint32_t pseudo_random(void); /** * Return with with uniform probability over specific range. */ uint32_t pseudo_random_range(uint32_t upper_bound); /** * Fill buffer with random bytes. */ void pseudo_random_bytes(void *dst, size_t count); /** * Set 128-bit seed. Following values will be * fully deterministic based on seed. */ void pseudo_random_seed(uint64_t a, uint64_t b); /* 128-bit state. Period: 2**128 - 1 */ uint64_t xorshift128plus(uint64_t *s0, uint64_t *s1); /* 1024-bit state. Period: 2**1024 - 1 */ uint64_t xorshift1024plus(uint64_t state[16], unsigned int counter); #endif pgbouncer-1.7/lib/usual/aatree.c0000664000175000017500000001644012511202014013565 00000000000000/* * AA-Tree - Binary tree with embeddable nodes. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Self-balancing binary tree. * * Here is an implementation of AA-tree (Arne Andersson tree) * which is simplification of Red-Black tree. * * Red-black tree has following properties that must be kept: * 1. A node is either red or black. * 2. The root is black. * 3. All leaves (NIL nodes) are black. * 4. Both childen of red node are black. * 5. Every path from root to leaf contains same number of black nodes. * * AA-tree adds additional property: * 6. Red node can exist only as a right node. * * Red-black tree properties quarantee that the longest path is max 2x longer * than shortest path (B-R-B-R-B-R-B vs. B-B-B-B) thus the tree will be roughly * balanced. Also it has good worst-case guarantees for insertion and deletion, * which makes it good tool for real-time applications. * * AA-tree removes most special cases from RB-tree, thus making resulting * code lot simpler. It requires slightly more rotations when inserting * and deleting but also keeps the tree more balanced. */ #include #include /* for NULL */ typedef struct AATree Tree; typedef struct AANode Node; /* * NIL node */ #define NIL ((struct AANode *)&_nil) static const struct AANode _nil = { NIL, NIL, 0 }; /* * Rebalancing. AA-tree needs only 2 operations * to keep the tree balanced. */ /* * Fix red on left. * * X Y * / --> \ * Y X * \ / * a a */ static inline Node * skew(Node *x) { Node *y = x->left; if (x->level == y->level && x != NIL) { x->left = y->right; y->right = x; return y; } return x; } /* * Fix 2 reds on right. * * X Y * \ / \ * Y --> X Z * / \ \ * a Z a */ static inline Node * split(Node *x) { Node *y = x->right; if (x->level == y->right->level && x != NIL) { x->right = y->left; y->left = x; y->level++; return y; } return x; } /* insert is easy */ static Node *rebalance_on_insert(Node *current) { return split(skew(current)); } /* remove is bit more tricky */ static Node *rebalance_on_remove(Node *current) { /* * Removal can create a gap in levels, * fix it by lowering current->level. */ if (current->left->level < current->level - 1 || current->right->level < current->level - 1) { current->level--; /* if ->right is red, change it's level too */ if (current->right->level > current->level) current->right->level = current->level; /* reshape, ask Arne about those */ current = skew(current); current->right = skew(current->right); current->right->right = skew(current->right->right); current = split(current); current->right = split(current->right); } return current; } /* * Recursive insertion */ static Node * insert_sub(Tree *tree, Node *current, uintptr_t value, Node *node) { int cmp; if (current == NIL) { /* * Init node as late as possible, to avoid corrupting * the tree in case it is already added. */ node->left = node->right = NIL; node->level = 1; tree->count++; return node; } /* recursive insert */ cmp = tree->node_cmp(value, current); if (cmp > 0) current->right = insert_sub(tree, current->right, value, node); else if (cmp < 0) current->left = insert_sub(tree, current->left, value, node); else /* already exists? */ return current; return rebalance_on_insert(current); } void aatree_insert(Tree *tree, uintptr_t value, Node *node) { tree->root = insert_sub(tree, tree->root, value, node); } /* * Recursive removal */ /* remove_sub could be used for that, but want to avoid comparisions */ static Node *steal_leftmost(Tree *tree, Node *current, Node **save_p) { if (current->left == NIL) { *save_p = current; return current->right; } current->left = steal_leftmost(tree, current->left, save_p); return rebalance_on_remove(current); } /* drop this node from tree */ static Node *drop_this_node(Tree *tree, Node *old) { Node *new = NIL; if (old->left == NIL) new = old->right; else if (old->right == NIL) new = old->left; else { /* * Picking nearest from right is better than from left, * due to asymmetry of the AA-tree. It will result in * less tree operations in the long run, */ old->right = steal_leftmost(tree, old->right, &new); /* take old node's place */ *new = *old; } /* cleanup for old node */ if (tree->release_cb) tree->release_cb(old, tree); tree->count--; return new; } static Node *remove_sub(Tree *tree, Node *current, uintptr_t value) { int cmp; /* not found? */ if (current == NIL) return current; cmp = tree->node_cmp(value, current); if (cmp > 0) current->right = remove_sub(tree, current->right, value); else if (cmp < 0) current->left = remove_sub(tree, current->left, value); else current = drop_this_node(tree, current); return rebalance_on_remove(current); } void aatree_remove(Tree *tree, uintptr_t value) { tree->root = remove_sub(tree, tree->root, value); } /* * Walking all nodes */ static void walk_sub(Node *current, enum AATreeWalkType wtype, aatree_walker_f walker, void *arg) { if (current == NIL) return; switch (wtype) { case AA_WALK_IN_ORDER: walk_sub(current->left, wtype, walker, arg); walker(current, arg); walk_sub(current->right, wtype, walker, arg); break; case AA_WALK_POST_ORDER: walk_sub(current->left, wtype, walker, arg); walk_sub(current->right, wtype, walker, arg); walker(current, arg); break; case AA_WALK_PRE_ORDER: walker(current, arg); walk_sub(current->left, wtype, walker, arg); walk_sub(current->right, wtype, walker, arg); break; } } /* walk tree in correct order */ void aatree_walk(Tree *tree, enum AATreeWalkType wtype, aatree_walker_f walker, void *arg) { walk_sub(tree->root, wtype, walker, arg); } /* walk tree in bottom-up order, so that walker can destroy the nodes */ void aatree_destroy(Tree *tree) { walk_sub(tree->root, AA_WALK_POST_ORDER, tree->release_cb, tree); /* reset tree */ tree->root = NIL; tree->count = 0; } /* prepare tree */ void aatree_init(Tree *tree, aatree_cmp_f cmpfn, aatree_walker_f release_cb) { tree->root = NIL; tree->count = 0; tree->node_cmp = cmpfn; tree->release_cb = release_cb; } /* * search function */ Node *aatree_search(Tree *tree, uintptr_t value) { Node *current = tree->root; while (current != NIL) { int cmp = tree->node_cmp(value, current); if (cmp > 0) current = current->right; else if (cmp < 0) current = current->left; else return current; } return NULL; } pgbouncer-1.7/lib/usual/netdb.h0000664000175000017500000000363012511202014013422 00000000000000/* * libusual - Utility library for C * * Copyright (c) 2010 Marko Kreen, Skype Technologies * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * * DNS lookup. */ #ifndef _USUAL_NETDB_H_ #define _USUAL_NETDB_H_ #include #ifdef HAVE_NETDB_H #include #endif #ifndef HAVE_GETADDRINFO_A /** Async execution */ #ifndef GAI_WAIT #define GAI_WAIT 0 #endif /** Synchronous execution */ #ifndef GAI_NOWAIT #define GAI_NOWAIT 1 #endif /* avoid name conflicts */ #define gaicb usual_gaicb #define getaddrinfo_a(a,b,c,d) usual_getaddrinfo_a(a,b,c,d) /** * Request data for getaddrinfo_a(). * * Fields correspond to getaddrinfo() parameters. */ struct gaicb { /** node name */ const char *ar_name; /** service name */ const char *ar_service; /** hints */ const struct addrinfo *ar_request; /** result */ struct addrinfo *ar_result; /* internal state */ int _state; }; #ifndef EAI_INPROGRESS #define EAI_INPROGRESS -100 #endif #ifndef EAI_SYSTEM #define EAI_SYSTEM -10 #endif #define gai_error(gcb) ((gcb)->_state) /** * Compat: Async DNS lookup. */ int getaddrinfo_a(int mode, struct gaicb *list[], int nitems, struct sigevent *sevp); #endif /* HAVE_GETADDRINFO_A */ #endif /* _USUAL_NETDB_H_ */ pgbouncer-1.7/lib/usual/aatree.h0000664000175000017500000000533512511202014013573 00000000000000/* * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * * AA-Tree - Binary tree with embeddable nodes. * * AA-Tree (Arne Andersson tree) is a simplified Red-Black tree. */ #ifndef _USUAL_AATREE_H_ #define _USUAL_AATREE_H_ #include struct AATree; struct AANode; /** Callback for node comparision against value */ typedef int (*aatree_cmp_f)(uintptr_t, struct AANode *node); /** Callback for walking the tree */ typedef void (*aatree_walker_f)(struct AANode *n, void *arg); /** * Tree header, for storing helper functions. */ struct AATree { struct AANode *root; int count; aatree_cmp_f node_cmp; aatree_walker_f release_cb; }; /** * Tree node. Embeddable, parent structure should be taken * with container_of(). * * Techinally, the full level is not needed and 2-lowest * bits of either ->left or ->right would be enough * to keep track of structure. Currently this is not * done to keep code simple. */ struct AANode { struct AANode *left; /**< smaller values */ struct AANode *right; /**< larger values */ int level; /**< number of black nodes to leaf */ }; /** * Walk order types. */ enum AATreeWalkType { AA_WALK_IN_ORDER = 0, /* left->self->right */ AA_WALK_PRE_ORDER = 1, /* self->left->right */ AA_WALK_POST_ORDER = 2, /* left->right->self */ }; /** Initialize structure */ void aatree_init(struct AATree *tree, aatree_cmp_f cmpfn, aatree_walker_f release_cb); /** Search for node */ struct AANode *aatree_search(struct AATree *tree, uintptr_t value); /** Insert new node */ void aatree_insert(struct AATree *tree, uintptr_t value, struct AANode *node); /** Remote node */ void aatree_remove(struct AATree *tree, uintptr_t value); /** Walk over all nodes */ void aatree_walk(struct AATree *tree, enum AATreeWalkType wtype, aatree_walker_f walker, void *arg); /** Free */ void aatree_destroy(struct AATree *tree); /** Check if terminal node. */ static inline int aatree_is_nil_node(const struct AANode *node) { return (node->left == node); } #endif pgbouncer-1.7/lib/usual/socket_win32.h0000664000175000017500000001336512511203511014651 00000000000000/* * Socket compat code for win32. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _USUAL_SOCKET_WIN32_H_ #define _USUAL_SOCKET_WIN32_H_ typedef int socklen_t; #define in_addr_t uint32_t /* * make recvmsg/sendmsg and fd related code compile */ struct iovec { void *iov_base; /* Base address. */ size_t iov_len; /* Length. */ }; struct msghdr { void *msg_name; int msg_namelen; struct iovec *msg_iov; int msg_iovlen; void *msg_control; int msg_controllen; int msg_flags; }; #ifndef SCM_RIGHTS #define SCM_RIGHTS 1 #endif #ifndef CMSG_FIRSTHDR struct cmsghdr { int cmsg_len; int cmsg_level; int cmsg_type; }; #define CMSG_DATA(cmsg) ((unsigned char *) ((struct cmsghdr *) (cmsg) + 1)) #define CMSG_ALIGN(len) (((len) + sizeof (size_t) - 1) \ & ~(sizeof (size_t) - 1)) #define CMSG_LEN(len) ((int)(CMSG_ALIGN(sizeof(struct cmsghdr))+(len))) #define CMSG_FIRSTHDR(mhdr) \ ((mhdr)->msg_controllen >= (int)sizeof(struct cmsghdr) ? \ (struct cmsghdr *)(mhdr)->msg_control : \ (struct cmsghdr *)NULL) #define CMSG_NXTHDR(mhdr, cmsg) \ (((cmsg) == NULL) ? CMSG_FIRSTHDR(mhdr) : \ (((u_char *)(cmsg) + CMSG_ALIGN((cmsg)->cmsg_len) \ + CMSG_ALIGN(sizeof(struct cmsghdr)) > \ (u_char *)((mhdr)->msg_control) + (mhdr)->msg_controllen) ? \ (struct cmsghdr *)NULL : \ (struct cmsghdr *)((u_char *)(cmsg) + CMSG_ALIGN((cmsg)->cmsg_len)))) #define CMSG_SPACE(len) (CMSG_ALIGN(sizeof(struct cmsghdr))+CMSG_ALIGN(len)) #endif /* * unify WSAGetLastError() with errno. * * and convert int <-> SOCKET. */ /* int <-> socket */ #define FD2S(fd) ((intptr_t)(fd)) #define S2FD(fd) ((int)(fd)) /* socket <-> HANDLE, plain casts */ #define FD2H(fd) ((HANDLE)FD2S(fd)) #define H2FD(h) S2FD((SOCKET)(h)) static inline int ewrap(int res) { if (res < 0) errno = WSAGetLastError(); return res; } /* proper signature for setsockopt */ static inline int w_setsockopt(int fd, int level, int optname, const void *optval, int optlen) { return ewrap(setsockopt(FD2S(fd), level, optname, optval, optlen)); } #define setsockopt(a,b,c,d,e) w_setsockopt(a,b,c,d,e) /* proper signature for send */ static inline ssize_t w_send(int fd, const void *buf, size_t len, int flags) { return ewrap(send(FD2S(fd), buf, len, flags)); } #define send(a,b,c,d) w_send(a,b,c,d) /* proper signature for recv */ static inline ssize_t w_recv(int fd, void *buf, size_t len, int flags) { return ewrap(recv(FD2S(fd), buf, len, flags)); } #define recv(a,b,c,d) w_recv(a,b,c,d) #define getsockopt(a,b,c,d,e) ewrap(getsockopt(FD2S(a),b,c,d,e)) #define connect(a,b,c) ewrap(connect(FD2S(a),b,c)) #define socket(a,b,c) ewrap(S2FD(socket(a,b,c))) #define bind(a,b,c) ewrap(bind(FD2S(a),b,c)) #define listen(a,b) ewrap(listen(FD2S(a),b)) #define accept(a,b,c) ewrap(accept(FD2S(a),b,c)) #define getpeername(a,b,c) ewrap(getpeername(FD2S(a),b,c)) #define getsockname(a,b,c) ewrap(getsockname(FD2S(a),b,c)) #define select(a,b,c,d,e) ewrap(select(a,b,c,d,e)) static inline struct hostent *w_gethostbyname(const char *n) { struct hostent *res = gethostbyname(n); if (!res) errno = WSAGetLastError(); return res; } #define gethostbyname(a) w_gethostbyname(a) /* make unix socket related code compile */ struct sockaddr_un { short sun_family; char sun_path[128]; }; /* sendmsg is not used */ static inline int sendmsg(int s, const struct msghdr *m, int flags) { if (m->msg_iovlen != 1) { errno = EINVAL; return -1; } return send(s, m->msg_iov[0].iov_base, m->msg_iov[0].iov_len, flags); } /* recvmsg() is, but only with one iov */ static inline int recvmsg(int s, struct msghdr *m, int flags) { if (m->msg_iovlen != 1) { errno = EINVAL; return -1; } if (m->msg_controllen) m->msg_controllen = 0; return recv(s, m->msg_iov[0].iov_base, m->msg_iov[0].iov_len, flags); } /* * fcntl */ #define F_GETFD 1 #define F_SETFD 2 #define F_GETFL 3 #define F_SETFL 4 #define O_NONBLOCK 1 #define FD_CLOEXEC HANDLE_FLAG_INHERIT static inline int fcntl(int fd, int cmd, long arg) { ULONG lval; DWORD dval; switch (cmd) { case F_GETFD: if (GetHandleInformation(FD2H(fd), &dval)) return dval; errno = EINVAL; return -1; case F_SETFD: /* set FD_CLOEXEC */ if (SetHandleInformation(FD2H(fd), FD_CLOEXEC, arg)) return 0; errno = EINVAL; return -1; case F_GETFL: /* O_NONBLOCK? */ return 0; case F_SETFL: /* set O_NONBLOCK */ lval = (arg & O_NONBLOCK) ? 1 : 0; if (ioctlsocket(FD2S(fd), FIONBIO, &lval) == SOCKET_ERROR) { errno = WSAGetLastError(); return -1; } return 0; default: errno = EINVAL; return -1; } } /* * SIO_KEEPALIVE_VALS for mingw32 */ #if !defined(SIO_KEEPALIVE_VALS) #define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR,4) struct tcp_keepalive { u_long onoff; u_long keepalivetime; u_long keepaliveinterval; }; #endif /* * Use native poll() if available */ #if !defined(HAVE_POLL) && defined(POLLIN) #define HAVE_POLL #define poll(a,b,c) usual_poll(a,b,c) static inline int poll(struct pollfd *fds, int nfds, int timeout) { return WSAPoll(fds, nfds, timeout); } #endif #endif pgbouncer-1.7/lib/usual/list.c0000664000175000017500000000373312511202014013300 00000000000000/* * Circular doubly linked list implementation. * * Copyright (c) 2010 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include /* merge 2 ordered arrays into one */ static struct List *merge(list_cmp_f cmp_func, struct List *p, struct List *q) { struct List res[1], *tail = res, *e; while (p && q) { if (cmp_func(p, q) <= 0) { e = p; p = p->next; } else { e = q; q = q->next; } tail->next = e; tail = e; } tail->next = p ? p : q; return res->next; } /* * non-recursive merge sort * * uses singly-linked NULL-terminated arrays internally. */ void list_sort(struct List *list, list_cmp_f cmp_func) { int i, top = 0; struct List *p; struct List *stack[64]; if (list_empty(list)) return; /* merge small sorted fragments into larger ones */ while (list->next != list) { p = list->next; list->next = p->next; p->next = NULL; for (i = 0; (i < top) && stack[i]; i++) { p = merge(cmp_func, stack[i], p); stack[i] = NULL; } stack[i] = p; if (i == top) top++; } /* merge remaining fragments */ for (p = NULL, i = 0; i < top; i++) p = merge(cmp_func, stack[i], p); /* restore proper List */ list->next = p; for (p = list; p->next; p = p->next) p->next->prev = p; list->prev = p; p->next = list; } pgbouncer-1.7/lib/usual/err.h0000664000175000017500000000375012511202014013121 00000000000000/* * Cmdline error reporting. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Error printing for command-line utilities. */ #ifndef _USUAL_ERR_H_ #define _USUAL_ERR_H_ #include #ifdef HAVE_ERR_H #include #endif #ifndef HAVE_ERR /** Print formatted message and strerror(errno) to stderr and exit with given error code */ void err(int e, const char *fmt, ...) _PRINTF(2, 3); #endif #ifndef HAVE_ERRX /** Print formatted message to stderr and exit with given error code */ void errx(int e, const char *fmt, ...) _PRINTF(2, 3); #endif #ifndef HAVE_WARN /** Print formatted message and strerror(errno) to stderr */ void warn(const char *fmt, ...) _PRINTF(1, 2); #endif #ifndef HAVE_WARNX /** Print formatted message to stderr */ void warnx(const char *fmt, ...) _PRINTF(1, 2); #endif #ifndef HAVE_SETPROGNAME /** Set program name to that will printed as prefix to error messages */ void setprogname(const char *s); #endif #ifndef HAVE_GETPROGNAME /** Return program name set with @ref setprogname */ const char *getprogname(void); #endif /** Malloc that exits on failure */ void *xmalloc(size_t len); /** Realloc that exits on failure */ void *xrealloc(void *p, size_t len); /** strdup that exits on failure */ char *xstrdup(const char *s); #endif pgbouncer-1.7/lib/usual/list.h0000664000175000017500000000757512511202014013315 00000000000000/* * Circular doubly linked list implementation. * * Copyright (c) 2007 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * Circular doubly linked list. */ #ifndef _USUAL_LIST_H_ #define _USUAL_LIST_H_ #include /** * Structure for both list nodes and heads. * * It is meant to be embedded in parent structure, * which can be acquired with container_of(). */ struct List { /** Pointer to next node or head. */ struct List *next; /** Pointer to previous node or head. */ struct List *prev; }; /** Define and initialize emtpy list head */ #define LIST(var) struct List var = { &var, &var } /** Initialize empty list head. */ static inline void list_init(struct List *list) { list->next = list->prev = list; } /** Is list empty? */ static inline int list_empty(const struct List *list) { return list->next == list; } /** Add item to the start of the list */ static inline struct List *list_prepend(struct List *list, struct List *item) { item->next = list->next; item->prev = list; list->next->prev = item; list->next = item; return item; } /** Add item to the end of the list */ static inline struct List *list_append(struct List *list, struct List *item) { item->next = list; item->prev = list->prev; list->prev->next = item; list->prev = item; return item; } /** Remove item from list */ static inline struct List *list_del(struct List *item) { item->prev->next = item->next; item->next->prev = item->prev; item->next = item->prev = item; return item; } /** Remove first from list and return */ static inline struct List *list_pop(struct List *list) { if (list_empty(list)) return NULL; return list_del(list->next); } /** Get first elem from list */ static inline struct List *list_first(const struct List *list) { if (list_empty(list)) return NULL; return list->next; } /** Get last elem from list */ static inline struct List *list_last(const struct List *list) { if (list_empty(list)) return NULL; return list->prev; } /** Remove first elem from list and return with casting */ #define list_pop_type(list, typ, field) \ (list_empty(list) ? NULL \ : container_of(list_del((list)->next), typ, field)) /** Loop over list */ #define list_for_each(item, list) \ for ((item) = (list)->next; \ (item) != (list); \ (item) = (item)->next) /** Loop over list backwards */ #define list_for_each_reverse(item, list) \ for ((item) = (list)->prev; \ (item) != (list); \ (item) = (item)->prev) /** Loop over list and allow removing item */ #define list_for_each_safe(item, list, tmp) \ for ((item) = (list)->next, (tmp) = (list)->next->next; \ (item) != (list); \ (item) = (tmp), (tmp) = (tmp)->next) /** Loop over list backwards and allow removing item */ #define list_for_each_reverse_safe(item, list, tmp) \ for ((item) = (list)->prev, (tmp) = (list)->prev->prev; \ (item) != (list); \ (item) = (tmp), (tmp) = (tmp)->prev) /** Comparator function signature for list_sort() */ typedef int (*list_cmp_f)(const struct List *a, const struct List *b); /** * Sort list. * * This implementation uses stable merge sort which operates in-place. */ void list_sort(struct List *list, list_cmp_f cmp_func); #endif pgbouncer-1.7/lib/usual/cbtree.c0000664000175000017500000001752612511203511013601 00000000000000/* * Crit-bit tree / binary radix tree. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Associates a C string with user pointer (called "obj"). * * Requires it's own internal nodes, thus not embeddable * to user structs. */ #include #include /* * - Childs are either other nodes or user pointers. * User pointers have lowest bit set. * * - All nodes have both childs. * * - Keys are handled as having infinite length, * zero-filled after actual end. */ struct Node { struct Node *child[2]; size_t bitpos; }; struct CBTree { struct Node *root; cbtree_getkey_func obj_key_cb; cbtree_walker_func obj_free_cb; void *cb_ctx; CxMem *cx; }; #define SAME_KEY SIZE_MAX #define MAX_KEY (SIZE_MAX / 8) /* * Low-level operations. */ /* does ptr point to user object or slot */ static inline bool is_node(void *ptr) { return ((uintptr_t)(ptr) & 1) == 0; } /* flag pointer as pointing to user object */ static inline void *set_external(const void *obj) { return (void*)((uintptr_t)(obj) | 1); } /* remove flag from user pointer */ static inline void *get_external(void *extval) { return (void*)((uintptr_t)(extval) & (~1)); } /* get specific bit from string */ static inline unsigned int get_bit(size_t bitpos, const unsigned char *key, size_t klen) { size_t pos = bitpos / 8; unsigned int bit = 7 - (bitpos % 8); return (pos < klen) && (key[pos] & (1 << bit)); } /* use callback to get key for a stored object */ static inline size_t get_key(struct CBTree *tree, void *obj, const void **key_p) { return tree->obj_key_cb(tree->cb_ctx, obj, key_p); } /* check if object key matches argument */ static inline bool key_matches(struct CBTree *tree, void *obj, const void *key, size_t klen) { const void *o_key; size_t o_klen; o_klen = get_key(tree, obj, &o_key); return (o_klen == klen) && (memcmp(key, o_key, klen) == 0); } /* Find first differing bit on 2 strings. */ static size_t find_crit_bit(const unsigned char *a, size_t alen, const unsigned char *b, size_t blen) { unsigned char av, bv, c, pos; size_t i; size_t minlen = (alen > blen) ? blen : alen; size_t maxlen = (alen > blen) ? alen : blen; /* find differing byte in common data */ for (i = 0; i < minlen; i++) { av = a[i]; bv = b[i]; if (av != bv) goto found; } /* find differing byte when one side is zero-filled */ for (; i < maxlen; i++) { av = (i < alen) ? a[i] : 0; bv = (i < blen) ? b[i] : 0; if (av != bv) goto found; } return SAME_KEY; found: /* calculate bits that differ */ c = av ^ bv; /* find the first one */ pos = 8 - fls(c); return i * 8 + pos; } /* * Lookup */ /* walk nodes until external pointer is found */ static void *raw_lookup(struct CBTree *tree, const void *key, size_t klen) { struct Node *node = tree->root; unsigned int bit; while (is_node(node)) { bit = get_bit(node->bitpos, key, klen); node = node->child[bit]; } return get_external(node); } /* actual lookup. returns obj ptr or NULL of not found */ void *cbtree_lookup(struct CBTree *tree, const void *key, size_t klen) { void *obj; if (!tree->root) return NULL; /* find match based on bits we know about */ obj = raw_lookup(tree, key, klen); /* need to check if the object actually matches */ if (key_matches(tree, obj, key, klen)) return obj; return NULL; } /* * Insertion. */ /* node allocation */ static struct Node *new_node(struct CBTree *tree) { struct Node *node = cx_alloc(tree->cx, sizeof(*node)); memset(node, 0, sizeof(*node)); return node; } /* insert into empty tree */ static bool insert_first(struct CBTree *tree, void *obj) { tree->root = set_external(obj); return true; } /* insert into specific bit-position */ static bool insert_at(struct CBTree *tree, size_t newbit, const void *key, size_t klen, void *obj) { /* location of current node/obj pointer under examination */ struct Node **pos = &tree->root; struct Node *node; unsigned int bit; while (is_node(*pos) && ((*pos)->bitpos < newbit)) { bit = get_bit((*pos)->bitpos, key, klen); pos = &(*pos)->child[bit]; } bit = get_bit(newbit, key, klen); node = new_node(tree); if (!node) return false; node->bitpos = newbit; node->child[bit] = set_external(obj); node->child[bit ^ 1] = *pos; *pos = node; return true; } /* actual insert: returns true -> insert ok or key found, false -> alloc failure */ bool cbtree_insert(struct CBTree *tree, void *obj) { const void *key, *old_key; size_t newbit, klen, old_klen; void *old_obj; if (!tree->root) return insert_first(tree, obj); /* current key */ klen = get_key(tree, obj, &key); if (klen > MAX_KEY) return false; /* nearest key in tree */ old_obj = raw_lookup(tree, key, klen); old_klen = get_key(tree, old_obj, &old_key); /* first differing bit is the target position */ newbit = find_crit_bit(key, klen, old_key, old_klen); if (newbit == SAME_KEY) return false; return insert_at(tree, newbit, key, klen, obj); } /* * Key deletion. */ /* true -> object was found and removed, false -> not found */ bool cbtree_delete(struct CBTree *tree, const void *key, size_t klen) { void *obj, *tmp; unsigned bit = 0; /* location of current node/obj pointer under examination */ struct Node **pos = &tree->root; /* if 'pos' has user obj, prev_pos has internal node pointing to it */ struct Node **prev_pos = NULL; if (!tree->root) return false; /* match bits we know about */ while (is_node(*pos)) { bit = get_bit((*pos)->bitpos, key, klen); prev_pos = pos; pos = &(*pos)->child[bit]; } /* does the key actually matches */ obj = get_external(*pos); if (!key_matches(tree, obj, key, klen)) return false; if (tree->obj_free_cb) tree->obj_free_cb(tree->cb_ctx, obj); /* drop the internal node pointing to our key */ if (prev_pos) { tmp = *prev_pos; *prev_pos = (*prev_pos)->child[bit ^ 1]; cx_free(tree->cx, tmp); } else { tree->root = NULL; } return true; } /* * Management. */ struct CBTree *cbtree_create(cbtree_getkey_func obj_key_cb, cbtree_walker_func obj_free_cb, void *cb_ctx, CxMem *cx) { struct CBTree *tree = cx_alloc(cx, sizeof(*tree)); if (!tree) return NULL; tree->root = NULL; tree->cb_ctx = cb_ctx; tree->obj_key_cb = obj_key_cb; tree->obj_free_cb = obj_free_cb; tree->cx = cx; return tree; } /* recursive freeing */ static void destroy_node(struct CBTree *tree, struct Node *node) { if (is_node(node)) { destroy_node(tree, node->child[0]); destroy_node(tree, node->child[1]); cx_free(tree->cx, node); } else if (tree->obj_free_cb) { void *obj = get_external(node); tree->obj_free_cb(tree->cb_ctx, obj); } } /* Free tree and all it's internal nodes. */ void cbtree_destroy(struct CBTree *tree) { if (tree->root) destroy_node(tree, tree->root); tree->root = NULL; cx_free(tree->cx, tree); } /* * walk over tree */ static bool walk(struct Node *node, cbtree_walker_func cb_func, void *cb_arg) { if (!is_node(node)) return cb_func(cb_arg, get_external(node)); return walk(node->child[0], cb_func, cb_arg) && walk(node->child[1], cb_func, cb_arg); } bool cbtree_walk(struct CBTree *tree, cbtree_walker_func cb_func, void *cb_arg) { if (!tree->root) return true; return walk(tree->root, cb_func, cb_arg); } pgbouncer-1.7/lib/usual/pgutil_kwlookup.g0000664000175000017500000000242512511202014015565 00000000000000/* gperf header for kwlookup */ %language=ANSI-C %readonly-tables %pic %enum %define lookup-function-name pg_keyword_lookup_real %define hash-function-name pg_keyword_lookup_hash %define string-pool-name pgkw %% all analyse analyze and any array as asc asymmetric authorization between bigint binary bit boolean both case cast char character check coalesce collate column concurrently constraint create cross current_catalog current_date current_role current_schema current_time current_timestamp current_user dec decimal default deferrable desc distinct do else end except exists extract false fetch float for foreign freeze from full grant greatest group having ilike in initially inner inout int integer intersect interval into is isnull join leading least left like limit localtime localtimestamp national natural nchar new none not notnull null nullif numeric off offset old on only or order out outer over overlaps overlay placing position precision primary real references returning right row select session_user setof similar smallint some substring symmetric table then time timestamp to trailing treat trim true union unique user using values varchar variadic verbose when where window with xmlattributes xmlconcat xmlelement xmlexists xmlforest xmlparse xmlpi xmlroot xmlserialize pgbouncer-1.7/lib/usual/logging.c0000664000175000017500000001457212615704007013774 00000000000000/* * Logging for unix service. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #ifdef HAVE_SYSLOG_H #include #endif #ifdef WIN32 #define LOG_EMERG 0 #define LOG_ALERT 1 #define LOG_CRIT 2 #define LOG_ERR 3 #define LOG_WARNING 4 #define LOG_NOTICE 5 #define LOG_INFO 6 #define LOG_DEBUG 7 #define LOG_PID 0 #define LOG_DAEMON 0 static inline void openlog(const char *ident, int opt, int fac) {} #define syslog win32_eventlog #define closelog() static void win32_eventlog(int level, const char *fmt, ...); #endif int cf_quiet = 0; int cf_verbose = 0; const char *cf_logfile = NULL; int cf_syslog = 0; const char *cf_syslog_ident = NULL; const char *cf_syslog_facility = NULL; enum LogLevel cf_syslog_level = LG_INFO; enum LogLevel cf_logfile_level = LG_NOISE; enum LogLevel cf_stderr_level = LG_NOISE; /* optional function to fill prefix */ logging_prefix_fn_t logging_prefix_cb; static FILE *log_file = NULL; static bool syslog_started = false; struct LevelInfo { const char *tag; int syslog_prio; }; static const struct LevelInfo log_level_list[] = { { "FATAL", LOG_CRIT }, /* LG_FATAL */ { "ERROR", LOG_ERR }, /* LG_ERROR */ { "WARNING", LOG_WARNING },/* LG_WARNING */ { "LOG", LOG_INFO }, /* LG_STATS*/ { "LOG", LOG_INFO }, /* LG_INFO */ { "DEBUG", LOG_DEBUG }, /* LG_DEBUG */ { "NOISE", LOG_DEBUG }, /* LG_NOISE */ }; struct FacName { const char *name; int code; }; static const struct FacName facility_names [] = { #ifndef WIN32 { "auth", LOG_AUTH }, #ifdef LOG_AUTHPRIV { "authpriv", LOG_AUTHPRIV }, #endif { "daemon", LOG_DAEMON }, { "user", LOG_USER }, { "local0", LOG_LOCAL0 }, { "local1", LOG_LOCAL1 }, { "local2", LOG_LOCAL2 }, { "local3", LOG_LOCAL3 }, { "local4", LOG_LOCAL4 }, { "local5", LOG_LOCAL5 }, { "local6", LOG_LOCAL6 }, { "local7", LOG_LOCAL7 }, #endif { NULL }, }; void reset_logging(void) { if (log_file) { fclose(log_file); log_file = NULL; } if (syslog_started) { closelog(); syslog_started = 0; } } static void start_syslog(void) { const struct FacName *f; int fac = LOG_DAEMON; const char *ident = cf_syslog_ident; if (!cf_syslog) return; if (cf_syslog_facility) { for (f = facility_names; f->name; f++) { if (strcmp(f->name, cf_syslog_facility) == 0) { fac = f->code; break; } } } if (!ident) { ident = getprogname(); if (!ident) ident = "unnamed"; } openlog(ident, LOG_PID, fac); syslog_started = 1; } void log_generic(enum LogLevel level, void *ctx, const char *fmt, ...) { char buf[2048], buf2[2048]; char ebuf[256]; char timebuf[64]; const struct LevelInfo *lev = &log_level_list[level]; unsigned pid = getpid(); va_list ap; int pfxlen = 0; int old_errno = errno; char *msg = buf; if (logging_prefix_cb) { pfxlen = logging_prefix_cb(level, ctx, buf, sizeof(buf)); if (pfxlen < 0) goto done; if (pfxlen >= (int)sizeof(buf)) pfxlen = sizeof(buf) - 1; } va_start(ap, fmt); vsnprintf(buf + pfxlen, sizeof(buf) - pfxlen, fmt, ap); va_end(ap); /* replace '\n' in message with '\n\t', strip trailing whitespace */ if (strchr(msg, '\n')) { char *dst = buf2; for (; *msg && dst - buf < (int)sizeof(buf2) - 2; msg++) { *dst++ = *msg; if (*msg == '\n') *dst++ = '\t'; } while (dst > buf2 && isspace(dst[-1])) dst--; *dst = 0; msg = buf2; } format_time_ms(0, timebuf, sizeof(timebuf)); if (!log_file && cf_logfile && cf_logfile[0]) { log_file = fopen(cf_logfile, "a"); if (log_file) { /* Got the file, disable buffering */ setvbuf(log_file, NULL, _IONBF, 0); } else { /* Unable to open, complain and fail */ fprintf(stderr, "%s %u %s Cannot open logfile: '%s': %s\n", timebuf, pid, log_level_list[0].tag, cf_logfile, strerror_r(errno, ebuf, sizeof(ebuf))); exit(1); } } if (!cf_quiet && level <= cf_stderr_level) fprintf(stderr, "%s %u %s %s\n", timebuf, pid, lev->tag, msg); if (log_file && level <= cf_logfile_level) fprintf(log_file, "%s %u %s %s\n", timebuf, pid, lev->tag, msg); if (cf_syslog && level <= cf_syslog_level) { if (!syslog_started) start_syslog(); syslog(lev->syslog_prio, "%s", msg); } done: if (old_errno != errno) errno = old_errno; } void log_fatal(const char *file, int line, const char *func, bool show_perror, void *ctx, const char *fmt, ...) { char buf[2048], ebuf[256]; const char *estr = NULL; int old_errno = 0; va_list ap; if (show_perror) { old_errno = errno; estr = strerror_r(errno, ebuf, sizeof(ebuf)); } va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); if (show_perror) { log_generic(LG_FATAL, ctx, "@%s:%d in function %s(): %s: %s [%d]", file, line, func, buf, estr, old_errno); } else { log_generic(LG_FATAL, ctx, "@%s:%d in function %s(): %s", file, line, func, buf); } } #ifdef WIN32 static void win32_eventlog(int level, const char *fmt, ...) { static HANDLE evtHandle = INVALID_HANDLE_VALUE; int elevel; char buf[1024]; const char *strlist[1] = { buf }; va_list ap; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); switch (level) { case LOG_CRIT: case LOG_ERR: elevel = EVENTLOG_ERROR_TYPE; break; case LOG_WARNING: elevel = EVENTLOG_WARNING_TYPE; break; default: elevel = EVENTLOG_INFORMATION_TYPE; } if (evtHandle == INVALID_HANDLE_VALUE) { evtHandle = RegisterEventSource(NULL, cf_syslog_ident); if (evtHandle == NULL || evtHandle == INVALID_HANDLE_VALUE) { evtHandle = INVALID_HANDLE_VALUE; return; } } ReportEvent(evtHandle, elevel, 0, 0, NULL, 1, 0, strlist, NULL); } #endif pgbouncer-1.7/lib/usual/fnmatch.c0000664000175000017500000001372212511203511013747 00000000000000/* * fnmatch.c * * Copyright (c) 2012 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Differences from POSIX: * - ^ can be used in place of ! * - \ is escape in bracket expression, unless FNM_NOESCAPE is given. * - FNM_CASEFOLD * - FNM_LEADING_DIR */ #include #include #include #ifdef NEED_USUAL_FNMATCH /* compare chars with case folding */ static inline bool cmp_fold(wchar_t c1, wchar_t c2, int flags) { if (c1 == c2) return true; if (flags & FNM_CASEFOLD) { if (iswupper(c1) && iswlower(c2)) return c1 == (wchar_t)towupper(c2); else if (iswlower(c1) && iswupper(c2)) return c1 == (wchar_t)towlower(c2); } return false; } /* compare char to range with case folding */ static inline bool range_fold(wchar_t c, wchar_t r1, wchar_t r2, int flags) { if (c >= r1 && c <= r2) return true; if (flags & FNM_CASEFOLD) { /* convert only if it makes sense */ if (iswupper(c) && iswlower(r1) && iswlower(r2)) { c = towlower(c); if (c >= r1 && c <= r2) return true; } else if (iswlower(c) && iswupper(r1) && iswupper(r2)) { c = towupper(c); if (c >= r1 && c <= r2) return true; } } return false; } /* match bracket expression */ static const wchar_t *match_class(const wchar_t *pat, wchar_t c, int flags) { const wchar_t *p = pat; const wchar_t *start; bool neg = false; bool match = false; bool fallback_ok = true; const wchar_t *n1, *n2; wctype_t wct; /* negation */ if (*p == '!' || *p == '^') { neg = true; p++; } start = p; loop: /* named class, equivalence class or collating symbol */ if (p[0] == '[' && (p[1] == ':' || p[1] == '.' || p[1] == '=')) { n1 = p + 2; n2 = wcschr(n1, p[1]); if (!n2 || n2[1] != ']') goto parse_fail; if (p[1] != ':') return NULL; p = n2 + 2; wct = wctype_wcsn(n1, n2-n1); if (wct == (wctype_t)0) return NULL; if (iswctype(c, wct)) match = true; fallback_ok = false; /* skip rest */ goto loop; } parse_fail: /* unexpected pattern end */ if (p[0] == '\0') { /* only open bracket exists, take it as literal */ if (fallback_ok && c == '[') return pat - 1; return NULL; } /* closing bracket */ if (p[0] == ']' && p != start) return (match ^ neg) ? p : NULL; /* escape next char */ if (p[0] == '\\' && !(flags & FNM_NOESCAPE)) { if (p[1] == '\0') return NULL; p++; } /* its either simple range or char */ if (p[1] == '-' && p[2] != ']' && p[2] != '\0') { wchar_t r1 = p[0]; wchar_t r2 = p[2]; if (r2 == '\\' && !(flags & FNM_NOESCAPE)) { p++; r2 = p[2]; if (r2 == '\0') return NULL; } if (range_fold(c, r1, r2, flags)) match = true; p += 3; } else { if (cmp_fold(c, p[0], flags)) match = true; p++; } goto loop; } /* * FNM_PATHNAME disallows wildcard match for '/', * FNM_PERIOD disallows wildcard match for leading '.', * check for string end also. */ static bool disallow_wildcard(const wchar_t *s, const wchar_t *str, int flags) { if (*s == '\0') return true; if (*s == '/') return (flags & FNM_PATHNAME); if (*s == '.' && (flags & FNM_PERIOD)) { if (s == str) return true; if (s[-1] == '/' && (flags & FNM_PATHNAME)) return true; } return false; } /* * Non-recursive fnmatch(), based on globmatch() by */ static int wfnmatch(const wchar_t *pat, const wchar_t *str, int flags) { const wchar_t *p = pat; const wchar_t *s = str; const wchar_t *retry_p = NULL; const wchar_t *skip_s = NULL; loop: switch (*p) { case '*': /* match any number of chars from this position on */ retry_p = p + 1; skip_s = s; /* dot after '*' must not match leading dot */ if (p[1] == '.' && disallow_wildcard(s, str, flags)) return FNM_NOMATCH; break; case '?': /* match any char */ if (disallow_wildcard(s, str, flags)) goto nomatch_retry; s++; break; case '[': /* match character class */ if (disallow_wildcard(s, str, flags)) goto nomatch_retry; p = match_class(p + 1, *s, flags); if (p == NULL) goto nomatch_retry; s++; break; case '\\': /* escape next char */ if (!(flags & FNM_NOESCAPE)) { p++; if (*p == '\0') return FNM_NOMATCH; } default: /* match single char */ if (*s == '/' && *p == '\0' && (flags & FNM_LEADING_DIR)) return 0; if (!cmp_fold(*p, *s, flags)) goto nomatch_retry; if (*s == '\0') return 0; s++; } p++; goto loop; nomatch_retry: /* eat chars with '*', if possible */ if (retry_p == NULL || *s == '\0') return FNM_NOMATCH; s = skip_s++; p = retry_p; if (*s == '\0') return (*p == '\0') ? 0 : FNM_NOMATCH; if (disallow_wildcard(s, str, flags)) return FNM_NOMATCH; s++; goto loop; } /* * Convert locale-specific encoding to wchar_t string */ int fnmatch(const char *pat, const char *str, int flags) { const wchar_t *wpat, *wstr; wchar_t pbuf[128]; wchar_t sbuf[128]; int plen = strlen(pat); int slen = strlen(str); int res; /* convert encoding */ wpat = mbstr_decode(pat, plen, NULL, pbuf, sizeof(pbuf) / sizeof(wchar_t), false); if (!wpat) return (errno == EILSEQ) ? FNM_NOMATCH : -1; wstr = mbstr_decode(str, slen, NULL, sbuf, sizeof(sbuf) / sizeof(wchar_t), true); if (!wstr) return -1; /* run actual fnmatch */ res = wfnmatch(wpat, wstr, flags); /* free buffers */ if (wstr != sbuf) free(wstr); if (wpat != pbuf) free(wpat); return res; } #endif pgbouncer-1.7/lib/usual/cfparser.h0000664000175000017500000001413012511202014014130 00000000000000/* * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * Config file parser. */ #ifndef _USUAL_CFPARSER_H_ #define _USUAL_CFPARSER_H_ #include /** * @name Simple line-by-line parser * @{ */ /** Callback signarure for @ref parse_ini_file() */ typedef bool (*cf_handler_f)(void *arg, bool is_sect, const char *key, const char *val); /** * Simple parser, launches callback for each line */ bool parse_ini_file(const char *fn, cf_handler_f user_handler, void *arg) _MUSTCHECK; /* @} */ /** * @name Complex parser with variable setting. * @{ */ /** @name Per-key flags * @{ */ /** The pointer is absolute */ #define CF_VAL_ABS 0 /** The pointer is relative to base */ #define CF_VAL_REL (1<<1) /** Value must not be changed on reload */ #define CF_NO_RELOAD (1<<2) /** Value can only be read */ #define CF_READONLY (1<<3) /** @} */ /** * Helper structure for passing key info to CfOps */ struct CfValue { void *value_p; const void *extra; const char *key_name; char *buf; int buflen; }; /** * Callbacks for setting and getting a variable value. * * Getter requires temp buf, returns string pointer, which * may or may not point to temp buf. Getter is optional. */ struct CfOps { bool (*setter)(struct CfValue *cv, const char *value); const char *(*getter)(struct CfValue *cv); const void *op_extra; }; /** * Parameter description */ struct CfKey { /** Parameter name */ const char *key_name; /** Type-specific functions, called with absolute pointer */ struct CfOps op; /** Flags: CF_VAL_ABS, CF_VAL_REL */ int flags; /** Absolute or relative offset */ uintptr_t key_ofs; /** Default value as string */ const char *def_value; }; /** * Section description */ struct CfSect { /** Section name */ const char *sect_name; /** Key list */ const struct CfKey *key_list; /** Get base pointer to dynamic sections (optional) */ void *(*base_lookup)(void *top_base, const char *sect_name); /** Set dynamic keys (optional) */ bool (*set_key)(void *base, const char *key, const char *val); /** Get dynamic keys (optional) */ const char *(*get_key)(void *base, const char *key, char *buf, int buflen); /** New section callback (optional) */ bool (*section_start)(void *top_base, const char *sect_name); }; /** * Top-level config information */ struct CfContext { /** Section list */ const struct CfSect *sect_list; /** Top-level base pointer, needed for relative addressing */ void *base; /** If set, then CF_NO_RELOAD keys cannot be changed anymore */ bool loaded; }; /** * @name Type-specific helpers * @{ */ /** Setter for string */ bool cf_set_str(struct CfValue *cv, const char *value); /** Setter for filename */ bool cf_set_filename(struct CfValue *cv, const char *value); /** Setter for int */ bool cf_set_int(struct CfValue *cv, const char *value); /** Setter for unsigned int */ bool cf_set_uint(struct CfValue *cv, const char *value); /** Setter for time-usec */ bool cf_set_time_usec(struct CfValue *cv, const char *value); /** Setter for time-double */ bool cf_set_time_double(struct CfValue *cv, const char *value); /** Setter for lookup */ bool cf_set_lookup(struct CfValue *cv, const char *value); /** Getter for string */ const char *cf_get_str(struct CfValue *cv); /** Getter for int */ const char *cf_get_int(struct CfValue *cv); /** Getter for unsigned int */ const char *cf_get_uint(struct CfValue *cv); /** Getter for time-usec */ const char *cf_get_time_usec(struct CfValue *cv); /** Getter for time-double */ const char *cf_get_time_double(struct CfValue *cv); /** Getter for int lookup */ const char *cf_get_lookup(struct CfValue *cv); /** @} */ /** * @name Shortcut CfOps for well-known types * @{ */ /** Ops for string */ #define CF_STR { cf_set_str, cf_get_str } /** Ops for filename */ #define CF_FILE { cf_set_filename, cf_get_str } /** Ops for integer */ #define CF_INT { cf_set_int, cf_get_int } /** Ops for unsigned integer */ #define CF_UINT { cf_set_uint, cf_get_uint } /** Ops for boolean */ #define CF_BOOL { cf_set_int, cf_get_int } /** Ops for time as usec */ #define CF_TIME_USEC { cf_set_time_usec, cf_get_time_usec } /** Ops for time as double */ #define CF_TIME_DOUBLE { cf_set_time_double, cf_get_time_double } /** Ops for lookup, takes table as argument */ #define CF_LOOKUP(t) { cf_set_lookup, cf_get_lookup, t } /** @} */ /** * Lookup entry for CF_LOOKUP table. */ struct CfLookup { const char *name; int value; }; /** * Helper to describe CfKey with absolute addressing */ #define CF_ABS(name, ops, var, flags, def) \ { name, ops, flags | CF_VAL_ABS, (uintptr_t)&(var), def } /** * Helper to describe CfKey with relative addressing. * * Before using it defined CF_REL_BASE to base struct. * * The var should be field in that struct. * * @code * struct Foo { * char *foo_name; * }; * #define CF_REL_BASE struct Foo * ... * CF_REL("name", CF_STR, foo_name, 0, NULL) * ... * #undef CF_REL_BASE * @endcode */ #define CF_REL(name, ops, var, flags, def) \ { name, ops, flags | CF_VAL_REL, offsetof(CF_REL_BASE, var), def } /** * Load config from file. */ bool cf_load_file(const struct CfContext *cf, const char *fn) _MUSTCHECK; /** * Get single value. */ const char *cf_get(const struct CfContext *cf, const char *sect, const char *var, char *buf, int buflen); /** * Set single value. */ bool cf_set(const struct CfContext *cf, const char *sect, const char *var, const char *val); /* @} */ #endif pgbouncer-1.7/lib/usual/utf8.h0000664000175000017500000000375412511203511013226 00000000000000/** @file * Low-level UTF8 handling. */ /* * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _USUAL_UTF8_H_ #define _USUAL_UTF8_H_ #include /** * Parse Unicode codepoint from UTF8 stream. * * On invalid UTF8 sequence returns negative byte value and * inreases src_p by one. * * @param src_p Location of data pointer. Will be incremented in-place. * @param srcend Pointer to end of data. * @return UNOCODE codepoint or negative byte value on error. */ int utf8_get_char(const char **src_p, const char *srcend); /** * Write Unicode codepoint as UTF8 sequence. * * Skips invalid Unicode values without error. * * @param c Unicode codepoint. * @param dst_p Location of dest pointer, will be increased in-place. * @param dstend Pointer to end of buffer. * @return false if not room, true otherwise. */ bool utf8_put_char(unsigned int c, char **dst_p, const char *dstend); /** Return UTF8 seq length based on unicode codepoint */ int utf8_char_size(unsigned int c); /** Return UTF8 seq length based on first byte */ int utf8_seq_size(unsigned char c); /** Return sequence length if all bytes are valid, 0 otherwise. */ int utf8_validate_seq(const char *src, const char *srcend); bool utf8_validate_string(const char *src, const char *end); #endif pgbouncer-1.7/lib/usual/config_msvc.h0000664000175000017500000000477012511203511014634 00000000000000 /* Define to 1 if you have the `event_base_new' function. */ #define HAVE_EVENT_BASE_NEW 1 /* Define to 1 if you have the `event_loopbreak' function. */ #define HAVE_EVENT_LOOPBREAK 1 /* Define to 1 if you have the header file. */ #define HAVE_MALLOC_H 1 /* Define to 1 if you have the header file. */ #define HAVE_MEMORY_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STDINT_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STDLIB_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STRING_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_STAT_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_TYPES_H 1 /* Define to the address where bug reports for this package should be sent. */ #define PACKAGE_BUGREPORT "https://libusual.github.com" /* Define to the full name of this package. */ #define PACKAGE_NAME "libusual" /* Define to the full name and version of this package. */ #define PACKAGE_STRING "libusual 0.1" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "libusual" /* Define to the home page for this package. */ #define PACKAGE_URL "" /* Define to the version of this package. */ #define PACKAGE_VERSION "0.1" /* Define to 1 if you have the ANSI C header files. */ #define STDC_HEADERS 1 /* Define to request cleaner win32 headers. */ #define WIN32_LEAN_AND_MEAN 1 /* Define to max win32 API version (0x0501=XP). */ //#define WINVER 0x0501 #define WINVER 0x0600 /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel). */ #if defined(_M_IX86) || defined(_M_X64) /* # undef WORDS_BIGENDIAN */ #else #error "Unsupported MSVC target CPU" #endif /* Define to `int' if doesn't define. */ #define gid_t int /* Define to `__inline__' or `__inline' if that's what the C compiler calls it, or to nothing if 'inline' is not supported under any name. */ #ifndef __cplusplus #define inline __inline #endif /* Define to `int' if does not define. */ #define pid_t int /* Define to the equivalent of the C99 'restrict' keyword, or to nothing if this is not supported. Do not define if restrict is supported directly. */ #ifndef restrict #define restrict #endif /* Define to `int' if doesn't define. */ #define uid_t int #define _CRT_SECURE_NO_WARNINGS 1 #ifndef WIN32 #define WIN32 1 #endif pgbouncer-1.7/lib/usual/mempool.c0000664000175000017500000000346312511202014013775 00000000000000/* * Simple memory pool for variable-length allocations. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include /* * Allows allocation of several variable-sized objects, * freeing them all together. * * ToDo: make it more 'obstack'-like (???) * - free_last * - resize_last * - append */ struct MemPool { struct MemPool *prev; unsigned size; unsigned used; }; void *mempool_alloc(struct MemPool **pool, unsigned size) { struct MemPool *cur = *pool; void *ptr; unsigned nsize; size = ALIGN(size); if (cur && cur->used + size <= cur->size) { ptr = (char *)(cur + 1) + cur->used; cur->used += size; return ptr; } else { nsize = cur ? (2 * cur->size) : 512; while (nsize < size) nsize *= 2; cur = calloc(1, sizeof(*cur) + nsize); if (cur == NULL) return NULL; cur->used = size; cur->size = nsize; cur->prev = *pool; *pool = cur; return (char *)(cur + 1); } } void mempool_destroy(struct MemPool **pool) { struct MemPool *cur, *tmp; if (!pool) return; for (cur = *pool, *pool = NULL; cur; ) { tmp = cur->prev; free(cur); cur = tmp; } } pgbouncer-1.7/lib/usual/pgsocket.c0000664000175000017500000002016712560223470014161 00000000000000/* * Async Postgres connection. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #define MAX_QRY_ARGS 32 /* PgSocket.wait_type */ enum WType { W_NONE = 0, W_SOCK, W_TIME }; typedef void (*libev_cb)(int sock, short flags, void *arg); struct PgSocket { /* libevent state */ struct event ev; /* track wait state */ enum WType wait_type; /* EV_READ / EV_WRITE */ uint8_t wait_event; /* should connect after sleep */ bool reconnect; /* current connection */ PGconn *con; /* user handler */ pgs_handler_f handler_func; void *handler_arg; /* saved connect string */ const char *connstr; /* custom base or NULL */ struct event_base *base; /* temp place for resultset */ PGresult *last_result; usec_t connect_time; usec_t lifetime; }; /* report event to user callback */ static void send_event(struct PgSocket *db, enum PgEvent ev) { db->handler_func(db, db->handler_arg, ev, NULL); } /* wait socket event from libevent */ static void wait_event(struct PgSocket *db, short ev, libev_cb fn) { Assert(!db->wait_type); event_set(&db->ev, PQsocket(db->con), ev, fn, db); if (db->base) event_base_set(db->base, &db->ev); if (event_add(&db->ev, NULL) < 0) fatal_perror("event_add"); db->wait_type = W_SOCK; db->wait_event = ev; } /* wait timeout from libevent */ static void timeout_cb(int sock, short flags, void *arg) { struct PgSocket *db = arg; db->wait_type = W_NONE; if (db->reconnect) { db->reconnect = false; pgs_connect(db); } else { send_event(db, PGS_TIMEOUT); } } /* some error happened */ static void conn_error(struct PgSocket *db, enum PgEvent ev, const char *desc) { log_error("connection error: %s", desc); log_error("libpq: %s", PQerrorMessage(db->con)); send_event(db, ev); } /* report previously stored result */ static void report_last_result(struct PgSocket *db) { PGresult *res = db->last_result; if (!res) return; db->last_result = NULL; switch (PQresultStatus(res)) { default: log_error("%s: %s", PQdb(db->con), PQresultErrorMessage(res)); case PGRES_COMMAND_OK: case PGRES_TUPLES_OK: case PGRES_COPY_OUT: case PGRES_COPY_IN: db->handler_func(db, db->handler_arg, PGS_RESULT_OK, res); } PQclear(res); } /* * Called when select() told that conn is avail for reading. * * It should call postgres handlers and then change state if needed. * * Because the callback may want to close the connection when processing * last resultset, the PGresult handover is delayed one step. */ static void result_cb(int sock, short flags, void *arg) { struct PgSocket *db = arg; PGresult *res; db->wait_type = W_NONE; if (!PQconsumeInput(db->con)) { conn_error(db, PGS_RESULT_BAD, "PQconsumeInput"); return; } /* loop until PQgetResult returns NULL */ while (db->con) { /* incomplete result? */ if (PQisBusy(db->con)) { wait_event(db, EV_READ, result_cb); return; } /* next result */ res = PQgetResult(db->con); if (!res) break; report_last_result(db); db->last_result = res; } report_last_result(db); } static void flush(struct PgSocket *db); static void send_cb(int sock, short flags, void *arg) { struct PgSocket *db = arg; db->wait_type = W_NONE; flush(db); } /* handle connect states */ static void connect_cb(int sock, short flags, void *arg) { struct PgSocket *db = arg; PostgresPollingStatusType poll_res; db->wait_type = W_NONE; poll_res = PQconnectPoll(db->con); switch (poll_res) { case PGRES_POLLING_WRITING: wait_event(db, EV_WRITE, connect_cb); break; case PGRES_POLLING_READING: wait_event(db, EV_READ, connect_cb); break; case PGRES_POLLING_OK: db->connect_time = get_time_usec(); send_event(db, PGS_CONNECT_OK); break; default: conn_error(db, PGS_CONNECT_FAILED, "PQconnectPoll"); } } /* send query to server */ static void flush(struct PgSocket *db) { int res = PQflush(db->con); if (res > 0) { wait_event(db, EV_WRITE, send_cb); } else if (res == 0) { wait_event(db, EV_READ, result_cb); } else conn_error(db, PGS_RESULT_BAD, "PQflush"); } /* override default notice receiver */ static void custom_notice_receiver(void *arg, const PGresult *res) { /* do nothing */ } /* * Public API */ struct PgSocket *pgs_create(const char *connstr, pgs_handler_f fn, void *handler_arg) { struct PgSocket *db; db = calloc(1, sizeof(*db)); if (!db) return NULL; db->handler_func = fn; db->handler_arg = handler_arg; db->connstr = strdup(connstr); if (!db->connstr) { pgs_free(db); return NULL; } return db; } void pgs_set_event_base(struct PgSocket *pgs, struct event_base *base) { pgs->base = base; } void pgs_set_lifetime(struct PgSocket *pgs, double lifetime) { pgs->lifetime = USEC * lifetime; } void pgs_connect(struct PgSocket *db) { if (db->con) pgs_disconnect(db); db->con = PQconnectStart(db->connstr); if (db->con == NULL) { conn_error(db, PGS_CONNECT_FAILED, "PQconnectStart"); return; } if (PQstatus(db->con) == CONNECTION_BAD) { conn_error(db, PGS_CONNECT_FAILED, "PQconnectStart"); return; } PQsetNoticeReceiver(db->con, custom_notice_receiver, db); wait_event(db, EV_WRITE, connect_cb); } void pgs_disconnect(struct PgSocket *db) { if (db->wait_type) { event_del(&db->ev); db->wait_type = W_NONE; db->reconnect = false; } if (db->con) { PQfinish(db->con); db->con = NULL; } if (db->last_result) { PQclear(db->last_result); db->last_result = NULL; } } void pgs_free(struct PgSocket *db) { if (db) { pgs_disconnect(db); free(db->connstr); free(db); } } void pgs_sleep(struct PgSocket *db, double timeout) { struct timeval tv; Assert(!db->wait_type); if (db->con && db->lifetime) { usec_t now = get_time_usec(); if (db->connect_time + db->lifetime < now) { pgs_disconnect(db); db->reconnect = true; } } tv.tv_sec = timeout; tv.tv_usec = (timeout - tv.tv_sec) * USEC; evtimer_set(&db->ev, timeout_cb, db); if (db->base) event_base_set(db->base, &db->ev); if (evtimer_add(&db->ev, &tv) < 0) fatal_perror("event_add"); db->wait_type = W_TIME; } void pgs_reconnect(struct PgSocket *db, double timeout) { pgs_disconnect(db); pgs_sleep(db, timeout); db->reconnect = true; } void pgs_send_query_simple(struct PgSocket *db, const char *q) { int res; log_noise("%s", q); res = PQsendQuery(db->con, q); if (!res) { conn_error(db, PGS_RESULT_BAD, "PQsendQuery"); return; } flush(db); } void pgs_send_query_params(struct PgSocket *db, const char *q, int cnt, ...) { int i; va_list ap; const char * args[MAX_QRY_ARGS]; if (cnt < 0 || cnt > MAX_QRY_ARGS) { log_warning("bad query arg cnt"); send_event(db, PGS_RESULT_BAD); return; } va_start(ap, cnt); for (i = 0; i < cnt; i++) args[i] = va_arg(ap, char *); va_end(ap); pgs_send_query_params_list(db, q, cnt, args); } void pgs_send_query_params_list(struct PgSocket *db, const char *q, int cnt, const char *args[]) { int res; log_noise("%s", q); res = PQsendQueryParams(db->con, q, cnt, NULL, args, NULL, NULL, 0); if (!res) { conn_error(db, PGS_RESULT_BAD, "PQsendQueryParams"); return; } flush(db); } int pgs_connection_valid(struct PgSocket *db) { return (db->con != NULL); } PGconn *pgs_get_connection(struct PgSocket *db) { return db->con; } bool pgs_waiting_for_reply(struct PgSocket *db) { if (!db->con) return false; if (PQstatus(db->con) != CONNECTION_OK) return false; return (db->wait_type == W_SOCK) && (db->wait_event == EV_READ); } pgbouncer-1.7/lib/usual/event.h0000664000175000017500000001114512511203511013452 00000000000000/* * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * libevent compat. * * This module adds few functions to older libevent versions, * or provides it's own libevent-compatible event loop * for cases where performance and features of full libevent * are not needed. */ #ifndef _USUAL_EVENT_H_ #define _USUAL_EVENT_H_ #include #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_LIBEVENT /* * Real libevent */ #include #ifndef HAVE_EVENT_BASE_NEW /** Compat: make sure event_base_new() always available */ static inline struct event_base *event_base_new(void) { return event_init(); } #endif #ifndef HAVE_EVENT_LOOPBREAK /** Compat: dummy event_loopbreak for libevent 1.3 */ static inline void event_loopbreak(void) { } #endif #else /* * internal libevent */ #include #include /** * Flags for event_set() / event_assign(): * EV_READ, EV_WRITE, EV_SIGNAL, EV_PERSIST * * Flags given to user callback: * EV_READ, EV_WRITE, EV_SIGNAL, EV_TIMEOUT. */ enum EventFlags { EV_TIMEOUT = 1, EV_READ = 2, EV_WRITE = 4, EV_SIGNAL = 8, EV_PERSIST = 16, }; /** Flags for event_loop() */ enum EventLoopType { EVLOOP_ONCE = 1, EVLOOP_NONBLOCK = 2, }; /** Event context. event_base contents are not open */ struct event_base; /** user callback signature */ typedef void (*uevent_cb_f)(int fd, short flags, void *arg); /** Read fd value from struct event */ #define EVENT_FD(ev) ((ev)->fd) /** Read signal value from struct event */ #define EVENT_SIGNAL(ev) ((ev)->fd) /** * Event structure for internal event loop. * * Although the struct is open, no direct accesses should be done. * Thus also the fields are incompatible with libevent. */ struct event { /* node for fd or signal lists */ struct List node; /* timeout info */ usec_t timeout_val; int timeout_idx; /* back-pointer into pollfd list */ int ev_idx; /* event base it is attached to */ struct event_base *base; /* user callback */ uevent_cb_f cb_func; void *cb_arg; /* fd or signal */ int fd; /* both user and internal flags */ short flags; }; struct event_base *event_init(void) _MUSTCHECK; struct event_base *event_base_new(void) _MUSTCHECK; void event_base_free(struct event_base *base); void event_set(struct event *ev, int fd, short flags, uevent_cb_f cb, void *arg); int event_loop(int loop_flags) _MUSTCHECK; int event_loopbreak(void); int event_add(struct event *ev, struct timeval *timeout) _MUSTCHECK; int event_del(struct event *ev); void event_assign(struct event *ev, struct event_base *base, int fd, short flags, uevent_cb_f cb, void *cb_arg); int event_base_loop(struct event_base *base, int loop_flags) _MUSTCHECK; int event_base_loopbreak(struct event_base *base); #define evtimer_set(ev, cb, arg) event_set(ev, -1, 0, cb, arg) #define evtimer_add(ev, tv) event_add(ev, tv) #define evtimer_del(ev) event_del(ev) #define signal_set(ev, sig, cb, arg) event_set(ev, sig, EV_SIGNAL | EV_PERSIST, cb, arg) #define signal_add(ev, tv) event_add(ev, tv) #define signal_del(ev) event_del(ev) /* random compat */ int event_once(int fd, short flags, uevent_cb_f cb_func, void *cb_arg, struct timeval *timeout); int event_base_once(struct event_base *base, int fd, short flags, uevent_cb_f cb_func, void *cb_arg, struct timeval *timeout); int event_loopexit(struct timeval *timeout); int event_base_loopexit(struct event_base *base, struct timeval *timeout); int event_base_set(struct event_base *base, struct event *ev); const char *event_get_version(void); const char *event_get_method(void); /* pointless compat */ #define event_dispatch() event_loop(0) #define event_base_dispatch(base) event_base_loop(base, 0) #define event_initialized(ev) is_event_active(ev) #define signal_initialized(ev) is_event_active(ev) #define evtimer_initialized(ev) is_event_active(ev) int is_event_active(struct event *ev); #endif /* internal libevent */ #endif /* _USUAL_EVENT_H_ */ pgbouncer-1.7/lib/usual/pthread.h0000664000175000017500000000401212511203511013753 00000000000000/* * Copyright (c) 2007-2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * * Pthreads compat for win32. */ #ifndef _USUAL_PTHREAD_H_ #define _USUAL_PTHREAD_H_ #include #ifdef HAVE_PTHREAD_H #include #else #ifdef WIN32 #define pthread_create(a,b,c,d) compat_pthread_create(a,b,c,d) #define pthread_mutex_init(a,b) compat_pthread_mutex_init(a,b) #define pthread_mutex_destroy(a) compat_pthread_mutex_destroy(a) #define pthread_mutex_lock(a) compat_pthread_mutex_lock(a) #define pthread_mutex_unlock(a) compat_pthread_mutex_unlock(a) #define pthread_join(a,b) compat_pthread_join(a,b) #define pthread_once(a,b) compat_pthread_once(a,b) typedef HANDLE pthread_t; typedef HANDLE pthread_mutex_t; typedef int pthread_attr_t; int pthread_create(pthread_t *t, pthread_attr_t *attr, void *(*fn)(void *), void *arg); int pthread_mutex_init(pthread_mutex_t *lock, void *unused); int pthread_mutex_destroy(pthread_mutex_t *lock); int pthread_mutex_lock(pthread_mutex_t *lock); int pthread_mutex_unlock(pthread_mutex_t *lock); int pthread_join(pthread_t *t, void **ret); #ifdef INIT_ONCE_STATIC_INIT #define PTHREAD_ONCE_INIT INIT_ONCE_STATIC_INIT typedef INIT_ONCE pthread_once_t; int pthread_once(pthread_once_t *once, void (*once_func)(void)); #endif #endif /* WIN32 */ #endif /* HAVE_PTHREAD_H */ #endif pgbouncer-1.7/lib/usual/fileutil.h0000664000175000017500000000332112511202014014140 00000000000000/* * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * File access utils. */ #ifndef _USUAL_FILEUTIL_H_ #define _USUAL_FILEUTIL_H_ #include #include /** Info about mapped file */ struct MappedFile { int fd; unsigned len; void *ptr; }; /** Signature for per-line callback */ typedef bool (*procline_cb)(void *arg, const char *line, ssize_t len); /** Read file into memory */ void *load_file(const char *fn, size_t *len_p); /** Loop over lines in file */ bool foreach_line(const char *fn, procline_cb proc_line, void *arg); /** Get file size */ ssize_t file_size(const char *fn); /** Map file into memory */ int map_file(struct MappedFile *m, const char *fname, int rw) _MUSTCHECK; /** Unmap previously mapped file */ void unmap_file(struct MappedFile *m); #if !defined(HAVE_GETLINE) #define getline(a,b,c) compat_getline(a,b,c) /** * Compat: Read line from file */ int getline(char **line_p, size_t *size_p, void *f); #endif #endif pgbouncer-1.7/lib/usual/psrandom.c0000664000175000017500000000745312567562476014213 00000000000000/* * Pseudo-random bytes. * * Copyright (c) 2015 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include /* Written in 2014 by Sebastiano Vigna (vigna@acm.org) To the extent possible under law, the author has dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. This software is distributed without any warranty. See . */ /* This is the fastest generator passing BigCrush without systematic failures, but due to the relatively short period it is acceptable only for applications with a mild amount of parallelism; otherwise, use a xorshift1024* generator. The state must be seeded so that it is not everywhere zero. If you have a nonzero 64-bit seed, we suggest to pass it twice through MurmurHash3's avalanching function. */ static inline uint64_t xorshift128plus_core(uint64_t a, uint64_t b, uint64_t *sb) { b ^= b << 23; b ^= a ^ (b >> 17) ^ (a >> 26); *sb = b; return a + b; } /* * End-user APIs for 128-bit and 1024-bit states. */ /* 128-bit state. Period: 2**128 - 1 */ uint64_t xorshift128plus(uint64_t *s0, uint64_t *s1) { /* swap s0 and s1, calculate new s1 */ uint64_t a = *s1, b = *s0; *s0 = a; return xorshift128plus_core(a, b, s1); } #define XS1K_STATE 16 #define XS1K_MASK (XS1K_STATE - 1) /* 1024-bit state. Period: 2**1024 - 1 */ uint64_t xorshift1024plus(uint64_t state[XS1K_STATE], unsigned int counter) { uint64_t *s0 = &state[counter & XS1K_MASK]; uint64_t *s1 = &state[(counter + 1) & XS1K_MASK]; return xorshift128plus_core(*s0, *s1, s1); } /* * csrandom()-style API on top that. */ static uint64_t ps_state[XS1K_STATE]; static uint32_t ps_init, ps_counter, ps_cache; static void ps_initial_seed(void) { csrandom_bytes(ps_state, sizeof ps_state); ps_init = 1; } void pseudo_random_seed(uint64_t a, uint64_t b) { uint64_t X1 = 123456789, X2 = 987654321; int i; /* xorshift does not like all-zero value */ if (a + X1) a += X1; if (b + X2) b += X2; /* fill all state */ for (i = XS1K_STATE - 1; i >= 0; i--) ps_state[i] = xorshift128plus(&a, &b); ps_init = 1; ps_counter = 0; } uint32_t pseudo_random(void) { uint64_t val; if (!ps_init) ps_initial_seed(); if (ps_init == 2) { ps_init = 1; return ps_cache; } val = xorshift1024plus(ps_state, ps_counter++); ps_cache = val >> 32; ps_init = 2; return val; } void pseudo_random_bytes(void *dst, size_t count) { uint32_t val; uint8_t *p = dst; while (count >= 4) { val = pseudo_random(); le32enc(p, val); count -= 4; p += 4; } if (count > 0) { for (val = pseudo_random(); count > 0; count--) { *p++ = val; val >>= 8; } } } uint32_t pseudo_random_range(uint32_t upper_bound) { uint32_t mod, lim, val; if (upper_bound <= 1) return 0; /* 2**32 % x == (2**32 - x) % x */ mod = -upper_bound % upper_bound; /* wait for value in range [0 .. 2**32-mod) */ lim = -mod; /* loop until good value appears */ while (1) { val = pseudo_random(); if (val < lim || lim == 0) return val % upper_bound; } } pgbouncer-1.7/lib/usual/signal.c0000664000175000017500000000545712511202014013607 00000000000000/* * Signal compat. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include /* * alarm() for win32 */ #ifdef WIN32 struct AlarmCtx { struct sigaction sa; HANDLE event; HANDLE thread; int secs; }; static volatile struct AlarmCtx actx; static DWORD WINAPI w32_alarm_thread(LPVOID arg) { DWORD wres; unsigned msecs; loop: if (actx.secs > 0) { msecs = actx.secs * 1000; } else { msecs = INFINITE; } wres = WaitForSingleObject(actx.event, msecs); if (wres == WAIT_OBJECT_0) { goto loop; } else if (wres == WAIT_TIMEOUT) { actx.secs = 0; if (actx.sa.sa_handler) actx.sa.sa_handler(SIGALRM); goto loop; } else { Sleep(1000); goto loop; } return 0; } unsigned int alarm(unsigned int secs) { actx.secs = secs; /* create event */ if (!actx.event) { actx.event = CreateEvent(NULL, FALSE, FALSE, NULL); if (!actx.event) return 0; } /* create or notify thread */ if (!actx.thread) { actx.thread = CreateThread(NULL, 0, w32_alarm_thread, NULL, 0, NULL); } else { SetEvent(actx.event); } return 0; } #endif #ifndef HAVE_SIGACTION int sigaction(int sig, const struct sigaction *sa, struct sigaction *old) { #ifdef WIN32 if (sig == SIGALRM) { if (old) *old = actx.sa; if (sa) actx.sa = *sa; else actx.sa.sa_handler = NULL; return 0; } #endif old->sa_handler = signal(sig, sa->sa_handler); if (old->sa_handler == SIG_ERR) return -1; return 0; } #endif #ifdef WIN32 /* Only sig=0 is supported, to detect if process is running (ESRCH->not) */ int kill(int pid, int sig) { HANDLE hProcess; DWORD exitCode; int ret = 0; /* handle only sig == 0 */ if (sig != 0) { errno = EINVAL; return -1; } hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid); if (hProcess == NULL) { if (GetLastError() == ERROR_INVALID_PARAMETER) ret = ESRCH; else ret = EPERM; } else { /* OpenProcess may succed for exited processes */ if (GetExitCodeProcess(hProcess, &exitCode)) { if (exitCode != STILL_ACTIVE) ret = ESRCH; } CloseHandle(hProcess); } if (ret) { errno = ret; return -1; } else return 0; } #endif pgbouncer-1.7/lib/usual/fnmatch.h0000664000175000017500000000320112511203511013743 00000000000000/* * fnmatch.h * * Copyright (c) 2012 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * \file * Theme include for strings. */ #ifndef _USUAL_FNMATCH_H_ #define _USUAL_FNMATCH_H_ #include #ifdef HAVE_FNMATCH_H #include #else #define NEED_USUAL_FNMATCH #endif #ifdef NEED_USUAL_FNMATCH #define fnmatch(p,s,f) usual_fnmatch(p,s,f) /** Do not allow wildcard to match '/' */ #define FNM_PATHNAME 1 /** Treat '\\' as literal value */ #define FNM_NOESCAPE 2 /** Do not allow wildcard to match leading '.' */ #define FNM_PERIOD 4 /** (GNU) Match case-insensitively */ #define FNM_CASEFOLD 8 /** (GNU) Match leading directory in path */ #define FNM_LEADING_DIR 16 /* (GNU) random alias */ #define FNM_FILE_NAME FNM_PATHNAME /** Returned on no match */ #define FNM_NOMATCH 1 /** * Compat: fnmatch() */ int fnmatch(const char *pat, const char *str, int flags); #endif /* NEED_USUAL_FNMATCH */ #endif /* !_USUAL_FNMATCH_H_ */ pgbouncer-1.7/lib/usual/mbuf.h0000664000175000017500000001726512511202014013270 00000000000000 /** \file * Safe and easy access to memory buffer. */ #ifndef _USUAL_MBUF_H_ #define _USUAL_MBUF_H_ #include #include /** MBuf structure. Allocated by user, can be in stack. */ struct MBuf { uint8_t *data; unsigned read_pos; unsigned write_pos; unsigned alloc_len; bool reader; bool fixed; }; /** Format fragment for *printf() */ #define MBUF_FMT ".*s" /** Argument layout for *printf() */ #define MBUF_ARG(m) ((m) ? mbuf_written(m) : 6), ((m) ? (const char *)mbuf_data(m) : "(null)") /* * Init functions */ /** Initialize R/O buffer to fixed memory area. */ static inline void mbuf_init_fixed_reader(struct MBuf *buf, const void *ptr, unsigned len) { buf->data = (uint8_t *)ptr; buf->read_pos = 0; buf->write_pos = len; buf->alloc_len = len; buf->reader = true; buf->fixed = true; } /** Initialize R/W buffer to fixed memory area. */ static inline void mbuf_init_fixed_writer(struct MBuf *buf, void *ptr, unsigned len) { buf->data = (uint8_t *)ptr; buf->read_pos = 0; buf->write_pos = 0; buf->alloc_len = len; buf->reader = false; buf->fixed = true; } /** Initialize R/W buffer to dynamically allocated memory area. */ static inline void mbuf_init_dynamic(struct MBuf *buf) { buf->data = NULL; buf->read_pos = 0; buf->write_pos = 0; buf->alloc_len = 0; buf->reader = false; buf->fixed = false; } /** Free dynamically allocated area, if exists. */ static inline void mbuf_free(struct MBuf *buf) { if (buf->data) { if (!buf->fixed) free(buf->data); memset(buf, 0, sizeof(*buf)); } } /* * Reset functions. */ /** Move read cursor to start of buffer. */ static inline void mbuf_rewind_reader(struct MBuf *buf) { buf->read_pos = 0; } /** Move both read and write cursor to start of buffer. */ static inline void mbuf_rewind_writer(struct MBuf *buf) { if (!buf->reader) { buf->read_pos = 0; buf->write_pos = 0; } } /* * Info functions. */ /** How many bytes can be read with read cursor. */ static inline unsigned mbuf_avail_for_read(const struct MBuf *buf) { return buf->write_pos - buf->read_pos; } /** How many bytes can be written with write cursor, without realloc. */ static inline unsigned mbuf_avail_for_write(const struct MBuf *buf) { if (!buf->reader && buf->alloc_len > buf->write_pos) return buf->alloc_len - buf->write_pos; return 0; } /** How many data bytes are in buffer. */ static inline unsigned mbuf_written(const struct MBuf *buf) { return buf->write_pos; } /** How many bytes have been read from buffer */ static inline unsigned mbuf_consumed(const struct MBuf *buf) { return buf->read_pos; } /** Return pointer to data area. */ static inline const void *mbuf_data(const struct MBuf *buf) { return buf->data; } /** Do the mbufs contain same data. */ static inline bool mbuf_eq(const struct MBuf *buf1, const struct MBuf *buf2) { if (buf1 == buf2) return true; if (!buf1 || !buf2 || (mbuf_written(buf1) != mbuf_written(buf2))) return false; return memcmp(mbuf_data(buf1), mbuf_data(buf2), mbuf_written(buf1)) == 0; } /** Complare mbuf to asciiz string */ static inline bool mbuf_eq_str(const struct MBuf *buf1, const char *s) { struct MBuf tmp; mbuf_init_fixed_reader(&tmp, s, strlen(s)); return mbuf_eq(buf1, &tmp); } /* * Read functions. */ /** Read a byte from read cursor. */ _MUSTCHECK static inline bool mbuf_get_byte(struct MBuf *buf, uint8_t *dst_p) { if (buf->read_pos + 1 > buf->write_pos) return false; *dst_p = buf->data[buf->read_pos++]; return true; } /** Read big-endian uint16 from read cursor. */ _MUSTCHECK static inline bool mbuf_get_char(struct MBuf *buf, char *dst_p) { if (buf->read_pos + 1 > buf->write_pos) return false; *dst_p = buf->data[buf->read_pos++]; return true; } _MUSTCHECK static inline bool mbuf_get_uint16be(struct MBuf *buf, uint16_t *dst_p) { unsigned a, b; if (buf->read_pos + 2 > buf->write_pos) return false; a = buf->data[buf->read_pos++]; b = buf->data[buf->read_pos++]; *dst_p = (a << 8) | b; return true; } /** Read big-endian uint32 from read cursor. */ _MUSTCHECK static inline bool mbuf_get_uint32be(struct MBuf *buf, uint32_t *dst_p) { unsigned a, b, c, d; if (buf->read_pos + 4 > buf->write_pos) return false; a = buf->data[buf->read_pos++]; b = buf->data[buf->read_pos++]; c = buf->data[buf->read_pos++]; d = buf->data[buf->read_pos++]; *dst_p = (a << 24) | (b << 16) | (c << 8) | d; return true; } /** Get reference to len bytes from read cursor. */ _MUSTCHECK static inline bool mbuf_get_uint64be(struct MBuf *buf, uint64_t *dst_p) { uint32_t a, b; if (!mbuf_get_uint32be(buf, &a) || !mbuf_get_uint32be(buf, &b)) return false; *dst_p = ((uint64_t)a << 32) | b; return true; } _MUSTCHECK static inline bool mbuf_get_bytes(struct MBuf *buf, unsigned len, const uint8_t **dst_p) { if (buf->read_pos + len > buf->write_pos) return false; *dst_p = buf->data + buf->read_pos; buf->read_pos += len; return true; } /** Get reference to asciiz string from read cursor. */ _MUSTCHECK static inline bool mbuf_get_chars(struct MBuf *buf, unsigned len, const char **dst_p) { if (buf->read_pos + len > buf->write_pos) return false; *dst_p = (char *)buf->data + buf->read_pos; buf->read_pos += len; return true; } _MUSTCHECK static inline bool mbuf_get_string(struct MBuf *buf, const char **dst_p) { const char *res = (char *)buf->data + buf->read_pos; const uint8_t *nul = memchr(res, 0, mbuf_avail_for_read(buf)); if (!nul) return false; *dst_p = res; buf->read_pos = nul + 1 - buf->data; return true; } /* * Write functions. */ /** Allocate more room if needed and the mbuf allows. */ _MUSTCHECK bool mbuf_make_room(struct MBuf *buf, unsigned len); /** Write a byte to write cursor. */ _MUSTCHECK static inline bool mbuf_write_byte(struct MBuf *buf, uint8_t val) { if (buf->write_pos + 1 > buf->alloc_len && !mbuf_make_room(buf, 1)) return false; buf->data[buf->write_pos++] = val; return true; } /** Write len bytes to write cursor. */ _MUSTCHECK static inline bool mbuf_write(struct MBuf *buf, const void *ptr, unsigned len) { if (buf->write_pos + len > buf->alloc_len && !mbuf_make_room(buf, len)) return false; memcpy(buf->data + buf->write_pos, ptr, len); buf->write_pos += len; return true; } /** writes full contents of another mbuf, without touching it */ _MUSTCHECK static inline bool mbuf_write_raw_mbuf(struct MBuf *dst, struct MBuf *src) { return mbuf_write(dst, src->data, src->write_pos); } /** writes partial contents of another mbuf, with touching it */ _MUSTCHECK static inline bool mbuf_write_mbuf(struct MBuf *dst, struct MBuf *src, unsigned len) { const uint8_t *data; if (!mbuf_get_bytes(src, len, &data)) return false; if (!mbuf_write(dst, data, len)) { src->read_pos -= len; return false; } return true; } /** Fiil mbuf with byte value */ _MUSTCHECK static inline bool mbuf_fill(struct MBuf *buf, uint8_t byte, unsigned len) { if (buf->write_pos + len > buf->alloc_len && !mbuf_make_room(buf, len)) return false; memset(buf->data + buf->write_pos, byte, len); buf->write_pos += len; return true; } /** remove some data from mbuf */ _MUSTCHECK static inline bool mbuf_cut(struct MBuf *buf, unsigned ofs, unsigned len) { if (buf->reader) return false; if (ofs + len < buf->write_pos) { unsigned endofs = ofs + len; memmove(buf->data + ofs, buf->data + endofs, buf->write_pos - endofs); buf->write_pos -= len; } else if (ofs < buf->write_pos) { buf->write_pos = ofs; } return true; } static inline void mbuf_copy(const struct MBuf *src, struct MBuf *dst) { *dst = *src; } _MUSTCHECK static inline bool mbuf_slice(struct MBuf *src, unsigned len, struct MBuf *dst) { if (len > mbuf_avail_for_read(src)) return false; mbuf_init_fixed_reader(dst, src->data + src->read_pos, len); src->read_pos += len; return true; } #endif pgbouncer-1.7/lib/usual/pgutil.h0000664000175000017500000000276612511202014013643 00000000000000/* * libusual - Utility library for C * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Utility functions for PostgreSQL data formats. */ #ifndef _USUAL_PGUTIL_H_ #define _USUAL_PGUTIL_H_ #include /** Check if string is reserver word for PostgreSQL. */ bool pg_is_reserved_word(const char *str); /** Quote value as string for PostgreSQL */ bool pg_quote_literal(char *_dst, const char *_src, int dstlen); /** Quote value as ident for PostgreSQL */ bool pg_quote_ident(char *_dst, const char *_src, int dstlen); /** Quote fully-qualified ident for PostgreSQL */ bool pg_quote_fqident(char *_dst, const char *_src, int dstlen); /** Parse PostgreSQL array. */ struct StrList *pg_parse_array(const char *pgarr, CxMem *cx); #endif pgbouncer-1.7/lib/usual/regex.c0000664000175000017500000006716212511203511013450 00000000000000/* * Small POSIX-only regex engine. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Simple recursive matcher, only features are small size * and POSIX compatibility. * * ERE syntax: . * ^ $ [] [[:cname:]] () {} | + ? * BRE syntax: . * ^ $ [] [[:cname:]] \(\) \{\} \1-9 * * With REG_RELAXED_SYNTAX, following common escapes will be available: * \b\B\d\D\s\S\w\W BRE: \| ERE: \1-9 * * With REG_RELAXED_MATCHING it returns the first match found after applying * leftmost-longest to all elements. It skips the combinatorics to turn it * into guaranteed-longest match. * * Skipped POSIX features: * - collation classes: [[. .]] * - equivalence classes: [[= =]] * - char ranges by locale order: [a-z] (byte order will be used) * - multi-byte chars: UTF-8 */ #include #ifndef USE_SYSTEM_REGEX #include #include #include #include #undef STRICT /* either dynamic or static decision */ #define STRICT (ctx->strict) /* how many regmatch_t can be reported */ #define MAX_GROUPS 128 /* max count we want to store, means 'infinite' for simple atoms */ #define MAX_COUNT 0x7fff /* max count for simple atoms: char, any or class */ #define SIMPLE_MAXCNT(op) (((op)->maxcnt == MAX_COUNT) ? 0x7FFFFFFF : (op)->maxcnt) #define is_word(c) (isalnum(c) || (c) == '_') struct Op; struct ExecCtx; struct GMatch; /* Operation type */ enum OpType { /* ops that take count */ OP_CHAR, OP_ANY, OP_CLASS, OP_GROUP, OP_BREF, /* ops that dont take count */ OP_BOL, OP_EOL, OP_WCHANGE, OP_NWCHANGE, OP_GMATCH, OP_FULLMATCH, }; #define NONCOUNT_OPS_START OP_BOL /* regex_t->internal */ struct RegexInt { struct Op *root; struct Op *glist; struct MemPool *pool; int flags; }; /* match function and its setter */ typedef int (*matcher_f)(struct ExecCtx *ctx, const struct Op *op, const char *str, struct GMatch *gm); static void set_op_type(struct Op *op, enum OpType op_type); /* List of tokens to be AND-ed together */ struct AndList { struct AndList *next; struct Op *op_list; }; /* extra data for group Op */ struct GroupData { struct Op *parent; /* parent group or NULL for first group */ struct AndList *or_list;/* alternative AndLists */ struct Op *glist_prev; /* prev group Op */ bool has_refs; /* if bref references it */ }; /* char class data */ struct ClassData { uint32_t bitmap[256 / 32]; }; /* operation data */ struct Op { struct Op *next; matcher_f matcher; uint16_t mincnt; uint16_t maxcnt; uint8_t type; union { uint8_t grp_no; /* OP_GROUP: group nr, 0-toplevel */ char lit; /* OP_CHAR */ uint8_t bref; /* OP_BREF */ }; union { struct ClassData cdata; struct GroupData gdata; }; }; #define OP_BASE (offsetof(struct Op, cdata)) /* * Operations on ClassData */ static bool class_isset(const struct ClassData *cd, unsigned char c) { return cd->bitmap[c / 32] & (1 << (c % 32)); } static void class_set(struct ClassData *cd, unsigned char c) { cd->bitmap[c / 32] |= (1 << (c % 32)); } static void class_negate(struct ClassData *cd) { int i; class_set(cd, 0); for (i = 0; i < 256/32; i++) cd->bitmap[i] ^= -1; } /* * Parsing code */ /* top-level context */ struct ParseCtx { regex_t *rx; struct RegexInt *rxi; struct Op *last_group; struct AndList *last_andlist; struct Op *last_elem; /* last op in current OR branch */ bool gotcnt; /* count was attached to last op */ bool strict; /* strict syntax */ }; static struct AndList *new_andlist(struct ParseCtx *ctx, struct Op *g) { struct AndList *al = mempool_alloc(&ctx->rxi->pool, sizeof(*al)); if (!al) return NULL; if (ctx->last_andlist) { ctx->last_andlist->next = al; } else { g->gdata.or_list = al; } ctx->last_andlist = al; return al; } static struct Op *new_op(struct ParseCtx *ctx, enum OpType t, int extra) { struct Op *op = mempool_alloc(&ctx->rxi->pool, OP_BASE + extra); if (!op) return NULL; set_op_type(op, t); op->mincnt = op->maxcnt = 1; ctx->gotcnt = false; /* append */ if (ctx->last_elem) { ctx->last_elem->next = op; } else if (ctx->last_andlist) { ctx->last_andlist->op_list = op; } else if (ctx->last_group) { struct AndList *alist; alist = new_andlist(ctx, ctx->last_group); if (!alist) return NULL; alist->op_list = op; } ctx->last_elem = op; if (t == OP_GROUP) { struct Op *parent = ctx->last_group; int gno = ++ctx->rx->re_nsub; op->grp_no = gno; op->gdata.parent = parent; op->gdata.glist_prev = ctx->rxi->glist; ctx->rxi->glist = op; ctx->last_group = op; ctx->last_andlist = NULL; ctx->last_elem = NULL; if (!ctx->rxi->root) ctx->rxi->root = op; } return op; } static int op_char(struct ParseCtx *ctx, unsigned c) { struct Op *op = new_op(ctx, OP_CHAR, 0); if (!op) return REG_ESPACE; op->lit = c; if ((ctx->rxi->flags & REG_ICASE) && isalpha(c)) op->lit = tolower(c); return 0; } static int op_bref(struct ParseCtx *ctx, unsigned c) { struct Op *g, *op; op = new_op(ctx, OP_BREF, 0); if (!op) return REG_ESPACE; op->bref = c - '0'; /* check if valid ref */ for (g = ctx->last_group; g; g = g->gdata.parent) { if (g->grp_no == op->bref) return REG_ESUBREG; } /* tag the group as referenced */ for (g = ctx->rxi->glist; g; g = g->gdata.glist_prev) { if (g->grp_no == op->bref) { g->gdata.has_refs = true; return 0; } } return REG_ESUBREG; } static int op_simple(struct ParseCtx *ctx, enum OpType t) { struct Op *op = new_op(ctx, t, 0); if (!op) return REG_ESPACE; return 0; } static int op_count_simple(struct ParseCtx *ctx, int min, int max) { struct Op *op = ctx->last_elem; if (!op || ctx->gotcnt) return REG_BADRPT; if (op->type >= NONCOUNT_OPS_START) return REG_BADRPT; ctx->gotcnt = true; op->mincnt = min; op->maxcnt = max; return 0; } static int op_count_full(struct ParseCtx *ctx, const char **re) { unsigned a, b; char *end = (char *)*re; bool ext = ctx->rxi->flags & REG_EXTENDED; int err; /* apply sanity check */ err = op_count_simple(ctx, 1, 1); if (err) return err; /* parse */ a = b = strtoul(*re, &end, 10); if (end == *re) return REG_EBRACE; if (*end == ',') { *re = end + 1; end = (char*)*re; b = strtoul(*re, &end, 10); if (end == *re) b = MAX_COUNT; } if (a > b || b > MAX_COUNT || a >= MAX_COUNT) return REG_BADBR; /* check for correct termination */ if (ext && end[0] == '}') { *re = end + 1; goto done; } else if (!ext && end[0] == '\\' && end[1] == '}') { *re = end + 2; goto done; } /* bad fmt, decide between error codes */ return strchr(end, '}') ? REG_BADBR : REG_EBRACE; done: ctx->last_elem->mincnt = a; ctx->last_elem->maxcnt = b; return 0; } static int op_gstart(struct ParseCtx *ctx) { struct Op *op; op = new_op(ctx, OP_GROUP, sizeof(struct GroupData)); if (!op) return REG_ESPACE; if (op->grp_no >= MAX_GROUPS) return REG_BADPAT; return 0; } static int finish_branch(struct ParseCtx *ctx) { int err; /* disallow empty OR fragments, but not empty groups */ if (!ctx->last_elem && ctx->last_andlist && STRICT) return REG_BADPAT; if (ctx->last_group->gdata.parent) err = op_simple(ctx, OP_GMATCH); else err = op_simple(ctx, OP_FULLMATCH); if (err) return err; ctx->last_elem = NULL; return 0; } static int op_gend(struct ParseCtx *ctx) { struct Op *op = ctx->last_group; struct AndList *alist; int err; if (!op) return REG_EPAREN; err = finish_branch(ctx); if (err) return err; ctx->last_group = op->gdata.parent; ctx->last_elem = op; /* recover previous andlist... */ alist = ctx->last_group->gdata.or_list; while (alist && alist->next) alist = alist->next; ctx->last_andlist = alist; return 0; } static int op_or(struct ParseCtx *ctx) { struct Op *gop = ctx->last_group; struct AndList *alist; int err; /* disallow empty OR branches */ if (!ctx->last_elem && STRICT) return REG_BADPAT; /* start new branch */ err = finish_branch(ctx); if (err) return err; alist = new_andlist(ctx, gop); if (!alist) return REG_ESPACE; ctx->last_andlist = alist; ctx->last_elem = NULL; return 0; } /* * Parse bracketed classes. */ static void add_char(struct ClassData *cd, unsigned char c, bool icase) { if (icase && isalpha(c)) { class_set(cd, tolower(c)); class_set(cd, toupper(c)); } else { class_set(cd, c); } } struct NamedClass { const char name[7]; unsigned char name_len; int (*check_func)(int c); }; static const struct NamedClass ctype_list[] = { { "alnum", 5, isalnum }, { "alpha", 5, isalpha }, { "blank", 5, isblank }, { "cntrl", 5, iscntrl }, { "digit", 5, isdigit }, { "graph", 5, isgraph }, { "lower", 5, islower }, { "print", 5, isprint }, { "punct", 5, ispunct }, { "space", 5, isspace }, { "upper", 5, isupper }, { "xdigit", 6, isxdigit }, }; static int fill_class(struct ClassData *cd, const char *name, const char **s_p, bool icase) { unsigned c; const struct NamedClass *cc = ctype_list; for (c = 0; c < ARRAY_NELEM(ctype_list); c++) { cc = ctype_list + c; if (strncmp(name, cc->name, cc->name_len) != 0) continue; name += cc->name_len; if (name[0] == ':' && name[1] == ']') goto found; break; } return *name ? REG_ECTYPE : REG_EBRACK; found: /* fill map */ for (c = 1; c < 256; c++) { if (cc->check_func(c)) add_char(cd, c, icase); } *s_p = name + 2; return 0; } #define MAP_RANGE 0x7FFF0001 #define MAP_END 0x7FFF0002 #define MAP_OTHER 0x7FFF0003 static int get_map_token(struct ParseCtx *ctx, const char **s_p, unsigned *dst_p, bool start, struct ClassData *cd, bool icase) { const char *s = *s_p; unsigned res; if (*s == '-') { if (start || s[1] == ']') res = '-'; else res = MAP_RANGE; s += 1; } else if (*s == ']' && !start) { res = MAP_END; s++; } else if (*s == '[' && (s[1] == '.' || s[1] == ':' || s[1] == '=')) { if (s[1] == ':') { s += 2; *dst_p = MAP_OTHER; return fill_class(cd, s, s_p, icase); } return REG_BADPAT; } else { res = (unsigned char)*s++; } *dst_p = res; *s_p = s; return 0; } static int op_class(struct ParseCtx *ctx, const char **re) { const char *s = *re; struct ClassData *cd; struct Op *op; bool not = false, icase = ctx->rxi->flags & REG_ICASE; const char *start; unsigned tk, c, prevtk = 0; bool is_range = false; int err; if (*s == '^') { s++; not = true; } start = s; op = new_op(ctx, OP_CLASS, sizeof(struct ClassData)); if (!op) return REG_ESPACE; cd = &op->cdata; if (not && (ctx->rxi->flags & REG_NEWLINE)) class_set(cd, '\n'); while (*s) { err = get_map_token(ctx, &s, &tk, s == start, cd, icase); if (err) return err; if (tk == MAP_END) { if (prevtk) add_char(cd, prevtk, icase); goto done; } else if (tk == MAP_OTHER) { if (is_range) return REG_ERANGE; if (prevtk) add_char(cd, prevtk, icase); prevtk = 0; } else if (tk == MAP_RANGE) { if (!prevtk) return REG_ERANGE; is_range = true; } else if (is_range) { if (tk < prevtk) return REG_ERANGE; for (c = prevtk; c <= tk; c++) add_char(cd, c, icase); is_range = false; prevtk = 0; } else { if (prevtk) add_char(cd, prevtk, icase); prevtk = tk; } } return REG_EBRACK; done: *re = s; if (not) class_negate(cd); return 0; } static int op_class_const(struct ParseCtx *ctx, const char *def) { const char *p = def + 1; return op_class(ctx, &p); } /* * Top-level syntax */ static int parse_relaxed_escapes(struct ParseCtx *ctx, char c) { if (STRICT) return REG_BADPAT; switch (c) { case 'b': return op_simple(ctx, OP_WCHANGE); case 'B': return op_simple(ctx, OP_NWCHANGE); case 'w': return op_class_const(ctx, "[_[:alnum:]]"); case 'W': return op_class_const(ctx, "[^_[:alnum:]]"); case 'd': return op_class_const(ctx, "[[:digit:]]"); case 'D': return op_class_const(ctx, "[^[:digit:]]"); case 's': return op_class_const(ctx, "[[:space:]]"); case 'S': return op_class_const(ctx, "[^[:space:]]"); } return REG_BADPAT; } static int parse_posix_ext(struct ParseCtx *ctx, const char *re) { int err = 0; unsigned c; int glevel = 0; loop: if (err) return err; c = *re++; switch (c) { case 0: return (glevel == 0) ? 0 : REG_EPAREN; case '(': glevel++; err = op_gstart(ctx); break; case ')': if (glevel > 0) { glevel--; err = op_gend(ctx); } else { err = op_char(ctx, c); /* POSIX bug */ } break; case '|': err = op_or(ctx); break; case '*': err = op_count_simple(ctx, 0, MAX_COUNT); break; case '?': err = op_count_simple(ctx, 0, 1); break; case '+': err = op_count_simple(ctx, 1, MAX_COUNT); break; case '[': err = op_class(ctx, &re); break; case '{': err = op_count_full(ctx, &re); break; case '.': err = op_simple(ctx, OP_ANY); break; case '^': err = op_simple(ctx, OP_BOL); break; case '$': err = op_simple(ctx, OP_EOL); break; case '\\': goto escaped; default: err = op_char(ctx, c); } goto loop; escaped: c = *re++; if (c == 0) err = REG_EESCAPE; else if (c >= '0' && c <= '9') err = STRICT ? REG_BADPAT : op_bref(ctx, c); else if (isalpha(c)) err = parse_relaxed_escapes(ctx, c); else err = op_char(ctx, c); goto loop; } static int parse_posix_basic(struct ParseCtx *ctx, const char *re) { int err = 0; unsigned c; int glevel = 0; loop: if (err) return err; c = *re++; switch (c) { case 0: return (glevel == 0) ? 0 : REG_EPAREN; case '*': if (ctx->last_elem && ctx->last_elem->type != OP_BOL) err = op_count_simple(ctx, 0, MAX_COUNT); else err = op_char(ctx, '*'); break; case '.': err = op_simple(ctx, OP_ANY); break; case '[': err = op_class(ctx, &re); break; case '^': if (!ctx->last_elem) err = op_simple(ctx, OP_BOL); else err = op_char(ctx, c); break; case '$': if (!*re || (re[0] == '\\' && re[1] == ')')) err = op_simple(ctx, OP_EOL); else err = op_char(ctx, c); break; case '\\': goto escaped; default: err = op_char(ctx, c); } goto loop; escaped: c = *re++; switch (c) { case 0: return REG_EESCAPE; case '(': glevel++; err = op_gstart(ctx); break; case ')': glevel--; if (glevel < 0) return REG_EPAREN; err = op_gend(ctx); break; case '{': err = op_count_full(ctx, &re); break; case '.': case '^': case '$': case '*': case '[': case ']': case '\\': err = op_char(ctx, c); break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': err = op_bref(ctx, c); break; case '|': err = STRICT ? REG_BADPAT : op_or(ctx); break; default: err = parse_relaxed_escapes(ctx, c); } goto loop; } /* * Public compiling API. */ int regcomp(regex_t *rx, const char *re, int flags) { struct ParseCtx ctx; struct RegexInt *rxi; int err; struct MemPool *pool = NULL; /* do it first, to allow regfree() */ memset(rx, 0, sizeof(*rx)); if (flags & ~(REG_EXTENDED | REG_ICASE | REG_NOSUB | REG_NEWLINE | REG_RELAXED)) return REG_BADPAT; if (!*re) return REG_BADPAT; rxi = mempool_alloc(&pool, sizeof(*rxi)); if (!rxi) return REG_ESPACE; rx->internal = rxi; rxi->pool = pool; /* initialize rx and local context */ memset(&ctx, 0, sizeof(ctx)); ctx.rx = rx; ctx.rxi = rxi; ctx.strict = !(flags & REG_RELAXED_SYNTAX); rxi->flags = flags; /* setup group #0 */ rx->re_nsub = -1; err = op_gstart(&ctx); if (err) goto failed; /* launch proper parser */ if (flags & REG_EXTENDED) err = parse_posix_ext(&ctx, re); else err = parse_posix_basic(&ctx, re); /* finalize group #0 */ if (!err) err = finish_branch(&ctx); /* relax if details are not needed */ if (flags & REG_NOSUB) { rxi->flags |= REG_RELAXED_MATCHING; rx->re_nsub = 0; } failed: /* clean up if problems */ if (err) regfree(rx); return err; } /* * Matching code */ /* historical best match */ struct HMatch { const char *hist_start; const char *hist_end; int rep_len; /* if repeated seq, full len thus far */ }; /* per-group-match context */ struct GMatch { struct GMatch *parent; /* parent group */ const struct Op *owner; /* Op for this group */ const char *start; /* match start */ const char *end; /* match end, NULL if no match */ struct GMatch *prevgm; /* older stack entry */ struct HMatch hm_next; /* best match for following stack entry */ int count; /* match nr in repeated seq */ }; /* top context */ struct ExecCtx { const regex_t *rx; const struct RegexInt *rxi; const char *str_start; regmatch_t *pmatch; int nmatch; int flags; bool strict; const char *last_endpos; struct HMatch hm_first[MAX_GROUPS]; struct GMatch *gm_stack[MAX_GROUPS]; struct GMatch *gm_cache[MAX_GROUPS]; }; static void push_gm(struct ExecCtx *ctx, struct GMatch *gm) { int gno = gm->owner->grp_no; gm->prevgm = ctx->gm_stack[gno]; ctx->gm_stack[gno] = gm; } static void pop_gm(struct ExecCtx *ctx, struct GMatch *gm) { int gno = gm->owner->grp_no; ctx->gm_stack[gno] = gm->prevgm; } static inline int do_match(struct ExecCtx *ctx, const struct Op *op, const char *str, struct GMatch *gm) { return op->matcher(ctx, op, str, gm); } static int scan_next(struct ExecCtx *ctx, const struct Op *op, const char *str, struct GMatch *gm, int curcnt, int alen) { int err = REG_NOMATCH; bool gotmatch = false; if (curcnt == op->mincnt) return do_match(ctx, op->next, str, gm); for (; curcnt >= op->mincnt; curcnt--) { err = do_match(ctx, op->next, str, gm); if (STRICT && err == 0) gotmatch = true; else if (err != REG_NOMATCH) break; str -= alen; } if (err == REG_NOMATCH && gotmatch) err = 0; return err; } static int match_char(struct ExecCtx *ctx, const struct Op *op, const char *str, struct GMatch *gm) { bool icase = (ctx->flags & REG_ICASE); int c, i, maxcnt = SIMPLE_MAXCNT(op); for (i = 0; (i < maxcnt) && str[i]; i++) { c = icase ? tolower((unsigned char)str[i]) : str[i]; if (c != op->lit) break; } return scan_next(ctx, op, str + i, gm, i, 1); } static int match_any(struct ExecCtx *ctx, const struct Op *op, const char *str, struct GMatch *gm) { bool nl = (ctx->flags & REG_NEWLINE); int i, maxcnt = SIMPLE_MAXCNT(op); for (i = 0; (i < maxcnt) && str[i]; i++) { if (nl && str[i] == '\n') break; } return scan_next(ctx, op, str + i, gm, i, 1); } static int match_class(struct ExecCtx *ctx, const struct Op *op, const char *str, struct GMatch *gm) { int i, maxcnt = SIMPLE_MAXCNT(op); for (i = 0; (i < maxcnt); i++) { if (!class_isset(&op->cdata, str[i])) break; } return scan_next(ctx, op, str + i, gm, i, 1); } static int match_bol(struct ExecCtx *ctx, const struct Op *op, const char *str, struct GMatch *gm) { if (str == ctx->str_start && !(ctx->flags & REG_NOTBOL)) return do_match(ctx, op->next, str, gm); else if (str != ctx->str_start && str[-1] == '\n' && (ctx->flags & REG_NEWLINE)) return do_match(ctx, op->next, str, gm); return REG_NOMATCH; } static int match_eol(struct ExecCtx *ctx, const struct Op *op, const char *str, struct GMatch *gm) { if (*str == '\n' && (ctx->flags & REG_NEWLINE)) return do_match(ctx, op->next, str, gm); else if (*str == 0 && !(ctx->flags & REG_NOTEOL)) return do_match(ctx, op->next, str, gm); return REG_NOMATCH; } static int match_wchange(struct ExecCtx *ctx, const struct Op *op, const char *str, struct GMatch *gm) { bool prevw = (str == ctx->str_start) ? false : is_word(str[-1]); bool curw = is_word(str[0]); bool ischange = prevw ^ curw; if ((op->type == OP_WCHANGE) ? ischange : !ischange) return do_match(ctx, op->next, str, gm); return REG_NOMATCH; } static int match_bref(struct ExecCtx *ctx, const struct Op *op, const char *str, struct GMatch *gm) { bool icase = ctx->flags & REG_ICASE; int i; struct GMatch *bgm = ctx->gm_stack[op->bref]; int blen = (bgm && bgm->end) ? (bgm->end - bgm->start) : -1; /* handle no-match, zero-len, zero-count */ if (blen < 0 && op->mincnt > 0) return REG_NOMATCH; if (blen <= 0 || op->maxcnt == 0) return do_match(ctx, op->next, str, gm); /* find max matches */ for (i = 0; (i < op->maxcnt) && *str; i++) { if (icase && strncasecmp(str, bgm->start, blen) != 0) break; else if (!icase && strncmp(str, bgm->start, blen) != 0) break; str += blen; } return scan_next(ctx, op, str, gm, i, blen); } static int match_group(struct ExecCtx *ctx, const struct Op *op, const char *str, struct GMatch *gm) { int err = REG_NOMATCH; bool gotmatch = false; struct GMatch gthis; /* per-group-match context */ memset(>his, 0, sizeof(gthis)); gthis.owner = op; gthis.start = str; gthis.parent = gm; if (gm && gm->owner == op) { gthis.parent = gm->parent; gthis.count = gm->count + 1; } gm = >his; push_gm(ctx, gm); if (op->maxcnt > 0) { struct AndList *alist = op->gdata.or_list; /* check all branches, unless relaxed matching */ while (alist) { err = do_match(ctx, alist->op_list, str, gm); if (err == 0 && STRICT) { gm->end = NULL; gotmatch = true; } else if (err != REG_NOMATCH) break; alist = alist->next; } } /* is no-match allowed? */ if ((op->mincnt == 0) && (gm->count == 0) && (err == REG_NOMATCH || (err == 0 && STRICT))) { gm->end = NULL; err = do_match(ctx, op->next, str, gm->parent); } pop_gm(ctx, gm); return gotmatch ? 0 : err; } static int match_gend(struct ExecCtx *ctx, const struct Op *f_op, const char *str, struct GMatch *gm) { int err = REG_NOMATCH; const struct Op *op = gm->owner; bool zeromatch = (str == gm->start); bool gotmatch = false; /* ignore follow-up empty matches, unless it has backrefs */ if (zeromatch && gm->count > 0 && gm->count >= op->mincnt && !gm->owner->gdata.has_refs) return REG_NOMATCH; /* tag as matched */ gm->end = str; /* try more repeats, stop if count full or last match was zero-length */ if (gm->count + 1 < op->maxcnt && !zeromatch) { err = match_group(ctx, op, str, gm); if (err == 0 && STRICT) gotmatch = true; else if (err != REG_NOMATCH) return err; } /* fail if not enough repeats */ if (!zeromatch && gm->count + 1 < op->mincnt) return err; /* continue with parent branch */ err = do_match(ctx, op->next, str, gm->parent); if (err == REG_NOMATCH && gotmatch) err = 0; return err; } /* * The juice of POSIX - match weighting. */ static int gmatch_hist_cmp(struct ExecCtx *ctx, int gno, struct GMatch *gm, int replen) { struct HMatch *hm = (gm->prevgm) ? &gm->prevgm->hm_next : &ctx->hm_first[gno]; int gmlen = (gm->end) ? (gm->end - gm->start) : -1; int hmlen = (hm->hist_end) ? (hm->hist_end - hm->hist_start) : -1; int gmreplen = (gmlen >= 0) ? (gmlen + replen) : replen; int hmreplen = ((hmlen >= 0) ? hmlen : 0) + hm->rep_len; int gmofs = (gm->end) ? (gm->start - ctx->str_start) : -1; int hmofs = (hm->hist_start) ? (hm->hist_start - ctx->str_start) : -1; /* prefer rightmost match, to allow preceding elements match more */ int res = (gmofs - hmofs); /* prefer longer repeated match */ if (res == 0 && gm->count == 0) res = (gmreplen - hmreplen); /* prefer longer single match */ if (res == 0) res = (gmlen - hmlen); return res; } static int cmp_gmatches(struct ExecCtx *ctx, int gno, struct GMatch *gm, int replen) { int cmp = 0, gmlen; if (gm) { /* need to compare preceding groups first */ gmlen = gm->end ? gm->end - gm->start : 0; cmp = cmp_gmatches(ctx, gno, gm->prevgm, (gm->count == 0) ? 0 : (replen + gmlen)); /* actual comparision */ if (!cmp) cmp = gmatch_hist_cmp(ctx, gno, gm, replen); } return cmp; } static int gm_resolve_tie(struct ExecCtx *ctx, int gno) { struct GMatch *gm = ctx->gm_stack[gno]; if (!gm) /* 0-count match is better than no match */ return ctx->hm_first[gno].hist_start ? -1 : 0; return cmp_gmatches(ctx, gno, gm, 0); } static void fill_history(struct ExecCtx *ctx, int gno) { struct HMatch *hm; int gmlen, rep_len = 0; struct GMatch *gm = ctx->gm_stack[gno]; while (STRICT && gm) { hm = (gm->prevgm) ? &gm->prevgm->hm_next : &ctx->hm_first[gno]; hm->hist_start = gm->start; hm->hist_end = gm->end; hm->rep_len = rep_len; gmlen = gm->end ? (gm->end - gm->start) : 0; rep_len += gmlen; if (gm->count == 0) rep_len = 0; gm = gm->prevgm; } } static void publish_gm(struct ExecCtx *ctx, int gno) { struct GMatch *gm = ctx->gm_stack[gno]; regmatch_t *rm = ctx->pmatch + gno; /* ignore non-matches */ while (gm && !gm->end) gm = gm->prevgm; /* require it to be inside reported parent */ if (gm && gm->parent) { int pno = gm->parent->owner->grp_no; if (gm->parent != ctx->gm_cache[pno]) gm = NULL; } ctx->gm_cache[gno] = gm; /* publish new match */ if (gm) { rm->rm_so = gm->start - ctx->str_start; rm->rm_eo = gm->end - ctx->str_start; } else { rm->rm_so = -1; rm->rm_eo = -1; } } /* compare and publish */ static int got_full_match(struct ExecCtx *ctx, const struct Op *f_op, const char *str, struct GMatch *gm) { int gno, cmp; /* tag group as matched */ gm->end = str; /* ignore shorter matches */ if (ctx->last_endpos && str < ctx->last_endpos) return 0; /* longer or equal length */ if (str > ctx->last_endpos) { ctx->last_endpos = str; goto better_match; } else if (STRICT && ctx->nmatch > 1) { for (gno = 0; gno < ctx->nmatch; gno++) { cmp = gm_resolve_tie(ctx, gno); if (cmp < 0) break; if (cmp > 0) goto better_match; } } return 0; better_match: for (gno = 0; gno < ctx->nmatch; gno++) { publish_gm(ctx, gno); fill_history(ctx, gno); } return 0; } /* fill in proper matcher */ static void set_op_type(struct Op *op, enum OpType op_type) { static const matcher_f mlist[] = { match_char, match_any, match_class, match_group, match_bref, match_bol, match_eol, match_wchange, match_wchange, match_gend, got_full_match }; op->matcher = mlist[op_type]; op->type = op_type; } /* * Public matching API */ int regexec(const regex_t *rx, const char *str, size_t nmatch, regmatch_t pmatch[], int eflags) { int err; struct ExecCtx ctx; if (eflags & ~(REG_NOTBOL | REG_NOTEOL)) return REG_BADPAT; /* init local context */ memset(&ctx, 0, sizeof(ctx)); ctx.pmatch = pmatch; ctx.nmatch = nmatch; ctx.str_start = str; ctx.rx = rx; ctx.rxi = rx->internal; ctx.flags = ctx.rxi->flags | eflags; /* reset pmatch area */ if (!(ctx.flags & REG_NOSUB)) memset(pmatch, -1, nmatch * sizeof(regmatch_t)); /* decide pmatch area that will be used */ if (!pmatch || (ctx.flags & REG_NOSUB)) ctx.nmatch = 0; else if (nmatch > (size_t)rx->re_nsub + 1) ctx.nmatch = rx->re_nsub + 1; ctx.strict = !(ctx.flags & REG_RELAXED_MATCHING) && (ctx.nmatch > 0); /* execute search */ str--; do { str++; err = do_match(&ctx, ctx.rxi->root, str, NULL); } while ((err == REG_NOMATCH) && *str); return err; } /* * Free parse tree */ void regfree(regex_t *rx) { struct RegexInt *rxi; if (rx) { rxi = rx->internal; if (rxi) mempool_destroy(&rxi->pool); memset(rx, 0, sizeof(*rx)); } } /* * Error strings */ size_t regerror(int err, const regex_t *rx, char *dst, size_t dstlen) { static const char errlist[][9] = { "NOERROR", /* 0 */ "NOMATCH", /* 1 */ "BADBR", /* 2 */ "BADPAT", /* 3 */ "BADRPT", /* 4 */ "EBRACE", /* 5 */ "EBRACK", /* 6 */ "ECOLLATE", /* 7 */ "ECTYPE", /* 8 */ "EESCAPE", /* 9 */ "EPAREN", /* 10 */ "ERANGE", /* 11 */ "ESPACE", /* 12 */ "ESUBREG", /* 13 */ }; const char *s = "EUNKNOWN"; if ((size_t)err < ARRAY_NELEM(errlist)) s = errlist[err]; return snprintf(dst, dstlen, "%s", s); } #endif /* !USE_SYSTEM_REGEX */ pgbouncer-1.7/lib/usual/safeio.h0000664000175000017500000000324112511202014013572 00000000000000/* * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * * EINTR-safe I/O functions. */ #ifndef _USUAL_SAFEIO_H_ #define _USUAL_SAFEIO_H_ #include /** read */ int safe_read(int fd, void *buf, int len) _MUSTCHECK; /** write */ int safe_write(int fd, const void *buf, int len) _MUSTCHECK; /** recv */ int safe_recv(int fd, void *buf, int len, int flags) _MUSTCHECK; /** send */ int safe_send(int fd, const void *buf, int len, int flags) _MUSTCHECK; /** close */ int safe_close(int fd); /** recvmsg */ int safe_recvmsg(int fd, struct msghdr *msg, int flags) _MUSTCHECK; /** sendmsg */ int safe_sendmsg(int fd, const struct msghdr *msg, int flags) _MUSTCHECK; /** connect */ int safe_connect(int fd, const struct sockaddr *sa, socklen_t sa_len) _MUSTCHECK; /** accept */ int safe_accept(int fd, struct sockaddr *sa, socklen_t *sa_len) _MUSTCHECK; #endif pgbouncer-1.7/lib/usual/mempool.h0000664000175000017500000000217112511202014013775 00000000000000/* * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * Simple memory pool for variable-length allocations. */ #ifndef _USUAL_MEMPOOL_H_ #define _USUAL_MEMPOOL_H_ #include /** Pool Reference */ struct MemPool; /** Allocate from pool */ void *mempool_alloc(struct MemPool **pool, unsigned size) _MALLOC; /** Release all memory in pool */ void mempool_destroy(struct MemPool **pool); #endif pgbouncer-1.7/lib/usual/base_win32.h0000664000175000017500000000637312556647420014320 00000000000000/* * Random win32 compat. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _USUAL_BASE_WIN32_H_ #define _USUAL_BASE_WIN32_H_ #include #include #include #include #ifndef ECONNABORTED #define ECONNABORTED WSAECONNABORTED #endif #ifndef EMSGSIZE #define EMSGSIZE WSAEMSGSIZE #endif #ifndef EINPROGRESS #define EINPROGRESS WSAEWOULDBLOCK /* WSAEINPROGRESS */ #endif #undef EAGAIN #define EAGAIN WSAEWOULDBLOCK /* WSAEAGAIN */ #ifndef EAFNOSUPPORT #define EAFNOSUPPORT ENOSYS #endif /* dummy types / functions */ #define hstrerror strerror #define getuid() (6667) #define setsid() getpid() #define setgid(x) (-1) #define setuid(x) (-1) #define fork() (-1) #define geteuid() getuid() #define setgroups(s, p) (-1) #define chown(f, u, g) (-1) #define srandom(s) srand(s) #define random() rand() #ifdef _MSC_VER #define snprintf(fmt, ...) _snprintf(fmt, __VA_ARGS__) static inline int strcasecmp(const char *a, const char *b) { return _stricmp(a, b); } static inline int strncasecmp(const char *a, const char *b, size_t cnt) { return _strnicmp(a, b, cnt); } typedef int ssize_t; #endif /* getrlimit() */ #define RLIMIT_NOFILE -1 struct rlimit { int rlim_cur; int rlim_max; }; static inline int getrlimit(int res, struct rlimit *dst) { dst->rlim_cur = dst->rlim_max = -1; return 0; } /* dummy getpwnam() */ struct passwd { char *pw_name; char *pw_passwd; uid_t pw_uid; pid_t pw_gid; char *pw_gecos; char *pw_dir; char *pw_shell; }; static inline struct passwd *getpwnam(const char *u) { return NULL; } static inline struct passwd *getpwuid(uid_t uid) { return NULL; } /* dummy getgrnam() */ struct group { char *gr_name; char *gr_passwd; gid_t gr_gid; char **gr_mem; }; static inline struct group *getgrnam(const char *g) { return NULL; } static inline struct group *getgrgid(gid_t gid) { return NULL; } /* format specifiers that should be in */ #ifndef HAVE_INTTYPES_H #define PRId8 "d" #define PRId16 "d" #define PRId32 "d" #define PRId64 "I64d" #define PRIi8 "d" #define PRIi16 "d" #define PRIi32 "d" #define PRIi64 "I64d" #define PRIo8 "o" #define PRIo16 "o" #define PRIo32 "o" #define PRIo64 "I64o" #define PRIu8 "u" #define PRIu16 "u" #define PRIu32 "u" #define PRIu64 "I64u" #define PRIx8 "x" #define PRIx16 "x" #define PRIx32 "x" #define PRIx64 "I64x" #define PRIX8 "X" #define PRIX16 "X" #define PRIX32 "X" #define PRIX64 "I64X" #endif #define PRIdZ "Id" #define PRIiZ "Ii" #define PRIoZ "Io" #define PRIuZ "Iu" #define PRIxZ "Ix" #define PRIXZ "IX" #endif pgbouncer-1.7/lib/usual/wchar.h0000664000175000017500000000242512511203511013436 00000000000000/* * wchar.h - wchar_t utilities. * * Copyright (c) 2012 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _USUAL_WCHAR_H_ #define _USUAL_WCHAR_H_ #include #include #include wchar_t *mbstr_decode(const char *str, int str_len, int *wlen_p, wchar_t *wbuf, int wbuf_len, bool allow_invalid); wctype_t wctype_wcsn(const wchar_t *name, unsigned int namelen); #ifndef HAVE_MBSNRTOWCS #define mbsnrtowcs(a,b,c,d,e) usual_mbsnrtowcs(a,b,c,d,e) size_t mbsnrtowcs(wchar_t *dst, const char **src_p, size_t srclen, size_t dstlen, mbstate_t *ps); #endif #endif pgbouncer-1.7/lib/usual/cfparser.c0000664000175000017500000003003312511203511014126 00000000000000/* * Config file parser. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #ifdef HAVE_PWD_H #include #endif #include #include #include #include #include #define MAX_INCLUDE 10 /* * INI file parser. */ static int count_lines(const char *s, const char *end) { int lineno = 1; for (; s < end; s++) { if (*s == '\n') lineno++; } return lineno; } static bool parse_ini_file_internal(const char *fn, cf_handler_f user_handler, void *arg, int inclevel) { char *buf; char *p, *key, *val; int klen, vlen; char o1, o2; bool ok; buf = load_file(fn, NULL); if (buf == NULL) return false; p = buf; while (*p) { /* space at the start of line - including empty lines */ while (*p && isspace(*p)) p++; if (strncmp(p, "%include", 8) == 0 && p[8] != 0 && isblank(p[8])) { if (inclevel >= MAX_INCLUDE) { log_error("include nesting level too deep (%s:%d), stopping loading", fn, count_lines(buf, p)); goto failed; } p += 8; while (*p && isblank(*p)) p++; /* now read value */ val = p; while (*p && (*p != '\n')) p++; vlen = p - val; /* eat space at end */ while (vlen > 0 && isspace(val[vlen - 1])) vlen--; /* * val now has the name of the file to be included. * Process it recursively. */ o1 = val[vlen]; val[vlen] = 0; log_debug("processing include: %s", val); ok = parse_ini_file_internal(val, user_handler, arg, inclevel + 1); val[vlen] = o1; if (!ok) goto failed; log_debug("returned to processing file %s", fn); continue; } /* skip comment lines */ if (*p == '#' || *p == ';') { while (*p && *p != '\n') p++; continue; } /* got new section */ if (*p == '[') { key = ++p; while (*p && *p != ']' && *p != '\n') p++; if (*p != ']') goto syntax_error; o1 = *p; *p = 0; log_debug("parse_ini_file: [%s]", key); ok = user_handler(arg, true, key, NULL); *p++ = o1; if (!ok) goto failed; continue; } /* done? */ if (*p == 0) break; /* read key val */ key = p; while (*p && (isalnum(*p) || strchr("_.-*", *p))) p++; klen = p - key; /* expect '=', skip it */ while (*p && (*p == ' ' || *p == '\t')) p++; if (*p != '=') { goto syntax_error; } else p++; while (*p && (*p == ' ' || *p == '\t')) p++; /* now read value */ val = p; while (*p && (*p != '\n')) p++; vlen = p - val; /* eat space at end */ while (vlen > 0 && isspace(val[vlen - 1])) vlen--; /* skip junk */ while (*p && isspace(*p)) p++; /* our buf is r/w, so take it easy */ o1 = key[klen]; o2 = val[vlen]; key[klen] = 0; val[vlen] = 0; log_debug("parse_ini_file: '%s' = '%s'", key, val); ok = user_handler(arg, false, key, val); log_debug("parse_ini_file: '%s' = '%s' ok:%d", key, val, ok); /* restore data, to keep count_lines() working */ key[klen] = o1; val[vlen] = o2; if (!ok) goto failed; } free(buf); return true; syntax_error: log_error("syntax error in configuration (%s:%d), stopping loading", fn, count_lines(buf, p)); failed: free(buf); return false; } bool parse_ini_file(const char *fn, cf_handler_f user_handler, void *arg) { return parse_ini_file_internal(fn, user_handler, arg, 0); } /* * Config framework. */ static void *get_dest(void *base, const struct CfKey *k) { char *dst; if (k->flags & CF_VAL_REL) { /* relative address requires base */ if (!base) return NULL; dst = (char *)base + k->key_ofs; } else dst = (char *)k->key_ofs; return dst; } static const struct CfSect *find_sect(const struct CfContext *cf, const char *name) { const struct CfSect *s; for (s = cf->sect_list; s->sect_name; s++) { if (strcmp(s->sect_name, name) == 0) return s; if (strcmp(s->sect_name, "*") == 0) return s; } return NULL; } static const struct CfKey *find_key(const struct CfSect *s, const char *key) { const struct CfKey *k; for (k = s->key_list; k->key_name; k++) { if (strcmp(k->key_name, key) == 0) return k; } return k; } const char *cf_get(const struct CfContext *cf, const char *sect, const char *key, char *buf, int buflen) { const struct CfSect *s; const struct CfKey *k; void *base, *p; struct CfValue cv; /* find section */ s = find_sect(cf, sect); if (!s) return NULL; /* find section base */ base = cf->base; if (s->base_lookup) base = s->base_lookup(base, sect); /* handle dynamic keys */ if (s->set_key) { if (!s->get_key) return NULL; return s->get_key(base, key, buf, buflen); } /* get fixed key */ k = find_key(s, key); if (!k || !k->op.getter) return NULL; p = get_dest(base, k); if (!p) return NULL; cv.key_name = k->key_name; cv.extra = k->op.op_extra; cv.value_p = p; cv.buf = buf; cv.buflen = buflen; return k->op.getter(&cv); } bool cf_set(const struct CfContext *cf, const char *sect, const char *key, const char *val) { const struct CfSect *s; const struct CfKey *k; void *base, *p; struct CfValue cv; /* find section */ s = find_sect(cf, sect); if (!s) { log_error("Unknown section: %s", sect); return false; } /* find section base */ base = cf->base; if (s->base_lookup) base = s->base_lookup(base, sect); /* handle dynamic keys */ if (s->set_key) return s->set_key(base, key, val); /* set fixed key */ k = find_key(s, key); if (!k) { log_error("Unknown parameter: %s/%s", sect, key); return false; } if (!k->op.setter || (k->flags & CF_READONLY)) { /* silently ignore */ return true; } if ((k->flags & CF_NO_RELOAD) && cf->loaded) { /* silently ignore */ return true; } p = get_dest(base, k); if (!p) { log_error("Bug - no base for relative key: %s/%s", sect, key); return false; } cv.key_name = k->key_name; cv.extra = k->op.op_extra; cv.value_p = p; cv.buf = NULL; cv.buflen = 0; return k->op.setter(&cv, val); } /* * File loader */ struct LoaderCtx { const struct CfContext *cf; const char *cur_sect; void *top_base; bool got_main_sect; }; static bool fill_defaults(struct LoaderCtx *ctx) { const struct CfKey *k; const struct CfSect *s; s = find_sect(ctx->cf, ctx->cur_sect); if (!s) goto fail; if (s == ctx->cf->sect_list) ctx->got_main_sect = true; if (s->section_start) { if (!s->section_start(ctx->top_base, ctx->cur_sect)) return false; } if (s->set_key) return true; for (k = s->key_list; k->key_name; k++) { if (!k->def_value || (k->flags & CF_READONLY)) continue; if ((k->flags & CF_NO_RELOAD) && ctx->cf->loaded) continue; if (!cf_set(ctx->cf, ctx->cur_sect, k->key_name, k->def_value)) goto fail; } return true; fail: log_error("fill_defaults fail"); return false; } static bool load_handler(void *arg, bool is_sect, const char *key, const char *val) { struct LoaderCtx *ctx = arg; if (is_sect) { if (ctx->cur_sect) free(ctx->cur_sect); ctx->cur_sect = strdup(key); if (!ctx->cur_sect) return false; return fill_defaults(ctx); } else if (!ctx->cur_sect) { log_error("load_init_file: value without section: %s", key); return false; } else { return cf_set(ctx->cf, ctx->cur_sect, key, val); } } bool cf_load_file(const struct CfContext *cf, const char *fn) { struct LoaderCtx ctx; bool ok; memset(&ctx, 0, sizeof(ctx)); ctx.cf = cf; ok = parse_ini_file(fn, load_handler, &ctx); if (ctx.cur_sect) free(ctx.cur_sect); if (ok && !ctx.got_main_sect) { log_error("load_init_file: main section missing from config file"); return false; } return ok; } /* * Various value parsers. */ bool cf_set_int(struct CfValue *cv, const char *value) { int *ptr = cv->value_p; char *end; long val; errno = 0; val = strtol(value, &end, 0); if (end == value || *end != 0) { /* reject partial parse */ if (!errno) errno = EINVAL; return false; } *ptr = val; return true; } bool cf_set_uint(struct CfValue *cv, const char *value) { unsigned int *ptr = cv->value_p; char *end; unsigned long val; errno = 0; val = strtoul(value, &end, 0); if (end == value || *end != 0) { /* reject partial parse */ if (!errno) errno = EINVAL; return false; } *ptr = val; return true; } bool cf_set_str(struct CfValue *cv, const char *value) { char **dst_p = cv->value_p; char *tmp = strdup(value); if (!tmp) { log_error("cf_set_str: no mem"); return false; } if (*dst_p) free(*dst_p); *dst_p = tmp; return true; } bool cf_set_filename(struct CfValue *cv, const char *value) { char **dst_p = cv->value_p; char *tmp, *home, *p; int v_len, usr_len, home_len; struct passwd *pw; /* do we need to do tilde expansion */ if (value[0] != '~') return cf_set_str(cv, value); /* find username end */ v_len = strlen(value); if ((p = memchr(value, '/', v_len)) == NULL) usr_len = v_len - 1; else usr_len = (p - value) - 1; if (usr_len) { p = malloc(usr_len + 1); if (!p) return false; memcpy(p, value + 1, usr_len); p[usr_len] = 0; pw = getpwnam(p); free(p); if (!pw) goto fail; home = pw->pw_dir; } else { home = getenv("HOME"); if (!home) { pw = getpwuid(getuid()); if (!pw) goto fail; home = pw->pw_dir; } } if (!home) goto fail; home_len = strlen(home); tmp = malloc(v_len - usr_len + home_len); if (!tmp) return false; memcpy(tmp, home, home_len); memcpy(tmp + home_len, value + usr_len + 1, v_len - usr_len - 1); tmp[v_len - 1 - usr_len + home_len] = 0; log_debug("expanded '%s' -> '%s'", value, tmp); if (*dst_p) free(*dst_p); *dst_p = tmp; return true; fail: log_error("cannot to expand filename: %s", value); return false; } /* parse float with error checking. returns -1 if failed */ static double parse_time(const char *value) { double v; char *endp = NULL; errno = 0; v = strtod_dot(value, &endp); if (errno) return -1; if (*endp || endp == value || v < 0) { errno = EINVAL; return -1; } return v; } bool cf_set_time_usec(struct CfValue *cv, const char *value) { usec_t *ptr = cv->value_p; double v = parse_time(value); if (v < 0) return false; *ptr = (usec_t)(USEC * v); return true; } bool cf_set_time_double(struct CfValue *cv, const char *value) { double *ptr = cv->value_p; double v = parse_time(value); if (v < 0) return false; *ptr = v; return true; } /* * Various value formatters. */ const char *cf_get_str(struct CfValue *cv) { char **p = cv->value_p; return *p; } const char *cf_get_int(struct CfValue *cv) { int *p = cv->value_p; snprintf(cv->buf, cv->buflen, "%d", *p); return cv->buf; } const char *cf_get_uint(struct CfValue *cv) { unsigned int *p = cv->value_p; snprintf(cv->buf, cv->buflen, "%u", *p); return cv->buf; } const char *cf_get_time_double(struct CfValue *cv) { double *p = cv->value_p; snprintf(cv->buf, cv->buflen, "%g", *p); return cv->buf; } const char *cf_get_time_usec(struct CfValue *cv) { struct CfValue tmp = *cv; usec_t *p = cv->value_p; double d = (double)(*p) / USEC; tmp.value_p = &d; return cf_get_time_double(&tmp); } /* * str->int mapping */ const char *cf_get_lookup(struct CfValue *cv) { int *p = cv->value_p; const struct CfLookup *lk = cv->extra; for (; lk->name; lk++) { if (lk->value == *p) return lk->name; } return "INVALID"; } bool cf_set_lookup(struct CfValue *cv, const char *value) { int *p = cv->value_p; const struct CfLookup *lk = cv->extra; for (; lk->name; lk++) { if (strcasecmp(lk->name, value) == 0) { *p = lk->value; return true; } } return false; } pgbouncer-1.7/lib/usual/bits.h0000664000175000017500000001263612571571744013326 00000000000000/* * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * Bit arithmetics. * * - is_power_of_2 * - ffs, ffsl, ffsll * - fls, flsl, flsll * - rol16, rol32, rol64 * - ror16, ror32, ror64 */ #ifndef _USUAL_BITS_H_ #define _USUAL_BITS_H_ #include #include #include /** Checks if integer has only one bit set */ static inline bool is_power_of_2(unsigned int n) { return (n > 0) && !(n & (n - 1)); } /* * Single-eval and type-safe rol/ror */ /** Rotate 16-bit int to left */ static inline uint16_t rol16(uint16_t v, int s) { return (v << s) | (v >> (16 - s)); } /** Rotate 32-bit int to left */ static inline uint32_t rol32(uint32_t v, int s) { return (v << s) | (v >> (32 - s)); } /** Rotate 64-bit int to left */ static inline uint64_t rol64(uint64_t v, int s) { return (v << s) | (v >> (64 - s)); } /** Rotate 16-bit int to right */ static inline uint16_t ror16(uint16_t v, int s) { return rol16(v, 16 - s); } /** Rotate 32-bit int to right */ static inline uint32_t ror32(uint32_t v, int s) { return rol32(v, 32 - s); } /** Rotate 64-bit int to right */ static inline uint64_t ror64(uint64_t v, int s) { return rol64(v, 64 - s); } /* * fls(int) * flsl(long) * flsll(long long) * * find MSB bit set, 1-based ofs, 0 if arg == 0 */ #undef fls #undef flsl #undef flsll #define fls(x) usual_fls(x) #define flsl(x) usual_flsl(x) #define flsll(x) usual_flsll(x) #if _COMPILER_GNUC(4,0) || __has_builtin(__builtin_clzll) #define _USUAL_FLS_(sfx, type) \ return (x == 0) ? 0 : ((8*sizeof(type)) - __builtin_clz ## sfx(x)) #else #define _USUAL_FLS_(sfx, type) \ unsigned type u = x; \ unsigned int bit; \ if (x == 0) return 0; \ /* count from smallest bit, assuming small values */ \ for (bit = 1; u > 1; bit++) u >>= 1; \ return bit #endif /** Find last (highest) set bit, 1-based offset, 0 if arg == 0 */ static inline int fls(int x) { _USUAL_FLS_(, int); } /** Find last (highest) set bit, 1-based offset, 0 if arg == 0 */ static inline int flsl(long x) { _USUAL_FLS_(l, long); } /** Find last (highest) set bit, 1-based offset, 0 if arg == 0 */ static inline int flsll(long long x) { _USUAL_FLS_(ll, long long); } #undef _USUAL_FLS_ /* * ffs(int) * ffsl(long) * ffsll(long long) * * find LSB bit set, 1-based ofs, 0 if arg == 0 */ #undef ffs #undef ffsl #undef ffsll #define ffs(x) usual_ffs(x) #define ffsl(x) usual_ffsl(x) #define ffsll(x) usual_ffsll(x) #if _COMPILER_GNUC(4,0) || __has_builtin(__builtin_ffsll) #define _USUAL_FFS_(sfx, type) \ return __builtin_ffs ## sfx((unsigned type)(x)) #else #define _USUAL_FFS_(sfx, type) \ unsigned int bit; \ unsigned type u = x; \ if (!x) return 0; \ /* count from smallest bit, assuming small values */ \ for (bit = 1; !(u & 1); bit++) u >>= 1; \ return bit #endif /** Find first (lowest) set bit, 1-based ofs, 0 if arg == 0 */ static inline int ffs(int x) { _USUAL_FFS_(, int); } /** Find first (lowest) set bit, 1-based ofs, 0 if arg == 0 */ static inline int ffsl(long x) { _USUAL_FFS_(l, long); } /** Find first (lowest) set bit, 1-based ofs, 0 if arg == 0 */ static inline int ffsll(long long x) { _USUAL_FFS_(ll, long long); } #undef _USUAL_FFS_ /* * Multiply and check overflow. */ #define _USUAL_MUL_SAFE_(type, max) \ type unsafe = (type)(1) << (sizeof(type) * 8/2); /* sqrt(max+1) */ \ if (a < unsafe && b < unsafe) \ goto safe; \ if (!a || !b) \ goto safe; \ if ((max / a) >= b) \ goto safe; \ return false; \ safe: \ *res_p = a * b; \ return true; /** Multiply with overflow check for 'unsigned int' */ static inline bool safe_mul_uint(unsigned int *res_p, unsigned int a, unsigned int b) { _USUAL_MUL_SAFE_(unsigned int, UINT_MAX); } /** Multiply with overflow check for 'unsigned long' */ static inline bool safe_mul_ulong(unsigned long *res_p, unsigned long a, unsigned long b) { _USUAL_MUL_SAFE_(unsigned long, ULONG_MAX); } /** Multiply with overflow check for 'uint8_t' */ static inline bool safe_mul_uint8(uint8_t *res_p, uint8_t a, uint8_t b) { _USUAL_MUL_SAFE_(uint8_t, UINT8_MAX); } /** Multiply with overflow check for 'uint16_t' */ static inline bool safe_mul_uint16(uint16_t *res_p, uint16_t a, uint16_t b) { _USUAL_MUL_SAFE_(uint16_t, UINT16_MAX); } /** Multiply with overflow check for 'uint32_t' */ static inline bool safe_mul_uint32(uint32_t *res_p, uint32_t a, uint32_t b) { _USUAL_MUL_SAFE_(uint32_t, UINT32_MAX); } /** Multiply with overflow check for 'uint64_t' */ static inline bool safe_mul_uint64(uint64_t *res_p, uint64_t a, uint64_t b) { _USUAL_MUL_SAFE_(uint64_t, UINT64_MAX); } /** Multiply with overflow check for 'size_t' */ static inline bool safe_mul_size(size_t *res_p, size_t a, size_t b) { _USUAL_MUL_SAFE_(size_t, SIZE_MAX); } #undef _USUAL_MUL_SAFE_ #endif pgbouncer-1.7/lib/usual/netdb.c0000664000175000017500000001104612511202014013415 00000000000000/* * libusual - Utility library for C * * Copyright (c) 2010 Marko Kreen, Skype Technologies * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include /* is compat function needed? */ #ifndef HAVE_GETADDRINFO_A /* full compat if threads are available */ #ifdef HAVE_PTHREAD #include #include /* * Basic blocking lookup */ static void gaia_lookup(pthread_t origin, struct gaicb *list[], int nitems, struct sigevent *sevp) { struct gaicb *g; int i, res; for (i = 0; i < nitems; i++) { g = list[i]; res = getaddrinfo(g->ar_name, g->ar_service, g->ar_request, &g->ar_result); g->_state = res; } if (!sevp || sevp->sigev_notify == SIGEV_NONE) { /* do nothing */ } else if (sevp->sigev_notify == SIGEV_SIGNAL) { /* send signal */ pthread_kill(origin, sevp->sigev_signo); } else if (sevp->sigev_notify == SIGEV_THREAD) { /* call function */ sevp->sigev_notify_function(sevp->sigev_value); } } /* * Thread to run blocking lookup in */ struct GAIAContext { struct List req_list; pthread_cond_t cond; pthread_mutex_t lock; pthread_t thread; }; struct GAIARequest { struct List node; pthread_t origin; int nitems; struct sigevent sev; struct gaicb *list[FLEX_ARRAY]; }; #define RQ_SIZE(n) (offsetof(struct GAIARequest,list) + (n)*(sizeof(struct gaicb *))) static void gaia_lock_reqs(struct GAIAContext *ctx) { pthread_mutex_lock(&ctx->lock); } static void gaia_unlock_reqs(struct GAIAContext *ctx) { pthread_mutex_unlock(&ctx->lock); } static void *gaia_lookup_thread(void *arg) { struct GAIAContext *ctx = arg; struct GAIARequest *rq; struct List *el; gaia_lock_reqs(ctx); while (1) { el = list_pop(&ctx->req_list); if (!el) { pthread_cond_wait(&ctx->cond, &ctx->lock); continue; } gaia_unlock_reqs(ctx); rq = container_of(el, struct GAIARequest, node); gaia_lookup(rq->origin, rq->list, rq->nitems, &rq->sev); free(rq); gaia_lock_reqs(ctx); } return NULL; } /* * Functions run in user thread */ static int gaia_post_request(struct GAIAContext *ctx, struct gaicb *list[], int nitems, struct sigevent *sevp) { struct GAIARequest *rq; rq = malloc(RQ_SIZE(nitems)); if (!rq) return EAI_MEMORY; list_init(&rq->node); rq->origin = pthread_self(); rq->nitems = nitems; if (sevp) rq->sev = *sevp; else rq->sev.sigev_notify = SIGEV_NONE; memcpy(rq->list, list, sizeof(struct gaicb *)); gaia_lock_reqs(ctx); list_append(&ctx->req_list, &rq->node); gaia_unlock_reqs(ctx); pthread_cond_signal(&ctx->cond); return 0; } static struct GAIAContext *gaia_create_context(void) { struct GAIAContext *ctx; int err; ctx = malloc(sizeof(*ctx)); if (!ctx) return NULL; list_init(&ctx->req_list); err = pthread_cond_init(&ctx->cond, NULL); if (err) goto failed; err = pthread_mutex_init(&ctx->lock, NULL); if (err) goto failed; err = pthread_create(&ctx->thread, NULL, gaia_lookup_thread, ctx); if (err) goto failed; return ctx; failed: free(ctx); errno = err; return NULL; } /* * Final interface */ int getaddrinfo_a(int mode, struct gaicb *list[], int nitems, struct sigevent *sevp) { static struct GAIAContext *ctx; if (nitems <= 0) return 0; if (sevp && sevp->sigev_notify != SIGEV_NONE && sevp->sigev_notify != SIGEV_SIGNAL && sevp->sigev_notify != SIGEV_THREAD) goto einval; if (mode == GAI_WAIT) { gaia_lookup(pthread_self(), list, nitems, sevp); return 0; } else if (mode == GAI_NOWAIT) { if (!ctx) { ctx = gaia_create_context(); if (!ctx) return EAI_MEMORY; } return gaia_post_request(ctx, list, nitems, sevp); } einval: errno = EINVAL; return EAI_SYSTEM; } #else /* without threads not much to do */ int getaddrinfo_a(int mode, struct gaicb *list[], int nitems, struct sigevent *sevp) { errno = ENOSYS; return EAI_SYSTEM; } #endif /* !HAVE_PTHREAD_H */ #endif /* !HAVE_GETADDRINFO_A */ pgbouncer-1.7/lib/usual/daemon.h0000664000175000017500000000220512511202014013566 00000000000000/** @file * Daemonization & pidfile handling. */ /* * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _USUAL_DAEMON_H_ #define _USUAL_DAEMON_H_ #include /** * Read a pid from pidfile and send a signal to it. */ bool signal_pidfile(const char *pidfile, int sig); /** * Daemonize process and write pidfile. */ void daemonize(const char *pidfile, bool go_background); #endif pgbouncer-1.7/lib/usual/pgsocket.h0000664000175000017500000000572412511202014014153 00000000000000/* * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * * Async Postgres connection framework. */ #ifndef _USUAL_PGSOCKET_H_ #define _USUAL_PGSOCKET_H_ #include #include /** * Event types reported to user handler function. */ enum PgEvent { /** Connection establishing finished */ PGS_CONNECT_OK, /** Connection establishing failed */ PGS_CONNECT_FAILED, /** Got result from query either resultset or DB error */ PGS_RESULT_OK, /** Query execution failed */ PGS_RESULT_BAD, /** Wakeup from timed sleep */ PGS_TIMEOUT, }; struct PgSocket; struct event_base; typedef void (*pgs_handler_f)(struct PgSocket *pgs, void *arg, enum PgEvent dbev, PGresult *res); /** Create PgSocket. * * It does not launch connection yet, use \ref pgs_connect() for that. * * @param connstr libpq connect string * @param fn callback function for event handling * @param arg extra context for callback * @return Initialized PgSocket structure */ struct PgSocket *pgs_create(const char *connstr, pgs_handler_f fn, void *arg); /** Release PgSocket */ void pgs_free(struct PgSocket *db); /** Change the event base for PgSocket */ void pgs_set_event_base(struct PgSocket *pgs, struct event_base *base); /** Set connection lifetime (in seconds) */ void pgs_set_lifetime(struct PgSocket *pgs, double lifetime); /** Launch connection */ void pgs_connect(struct PgSocket *db); /** Drop connection */ void pgs_disconnect(struct PgSocket *db); /** Send simple query */ void pgs_send_query_simple(struct PgSocket *db, const char *query); /** Send extended query, args from varargs */ void pgs_send_query_params(struct PgSocket *db, const char *query, int nargs, ...); /** Send extended query, args from list */ void pgs_send_query_params_list(struct PgSocket *db, const char *query, int nargs, const char *argv[]); /** Ignore the connection for specified time */ void pgs_sleep(struct PgSocket *db, double timeout); /** Disconnect, sleep, reconnect */ void pgs_reconnect(struct PgSocket *db, double timeout); /** Does PgSocket have established connection */ int pgs_connection_valid(struct PgSocket *db); /** Return underlying Postgres connection */ PGconn *pgs_get_connection(struct PgSocket *db); bool pgs_waiting_for_reply(struct PgSocket *db); #endif pgbouncer-1.7/lib/usual/hashtab-impl.h0000664000175000017500000001352212511203511014703 00000000000000/* * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * Simple customizable hashtable implementation. * * - Fixed-size hash table, open-addressed * - Extended by linking several together * - Resizable by copying. * - Can be lockless in multi-reader, one-writer situation if * mempory barrier macros are defined. This also requires that * HashItem must not be split across cachelines. */ #include #include #ifndef HTAB_KEY_T /** Overridable type for key */ #define HTAB_KEY_T unsigned long #endif #ifndef HTAB_VAL_T /** Overridable type for value */ #define HTAB_VAL_T void * #endif #ifndef HTAB_RMB #define HTAB_RMB #endif #ifndef HTAB_WMB #define HTAB_WMB #endif /** Typedef for key */ typedef HTAB_KEY_T htab_key_t; /** Typedef for value */ typedef HTAB_VAL_T htab_val_t; #ifndef HTAB_ITEM #define HTAB_ITEM /** HashTab slot */ struct HashItem { htab_key_t key; htab_val_t value; }; #endif /** Signature for comparision function */ typedef bool (*hash_cmp_fn)(const htab_val_t curval, const void *arg); #ifndef HTAB_MAX_FILL /** Max fill percentage */ #define HTAB_MAX_FILL 75 #endif #define MASK(h) ((h)->size - 1) #define CALC_POS(h, key) ((key) & MASK(h)) #define NEXT_POS(h, pos) (((pos) * 5 + 1) & MASK(h)) #define MAX_USED(h) ((h)->size * HTAB_MAX_FILL / 100) /** Single HashTab segment */ struct HashTab { struct HashTab *next; hash_cmp_fn cmp_fn; CxMem *ca; unsigned size; unsigned used; struct HashItem tab[FLEX_ARRAY]; }; /** Initialize HashTab */ static struct HashTab *hashtab_create(unsigned size, hash_cmp_fn cmp_fn, CxMem *ca) { struct HashTab *h; unsigned len = size * sizeof(struct HashItem) + offsetof(struct HashTab, tab); h = cx_alloc0(ca, len); if (h) { h->size = size; h->cmp_fn = cmp_fn; h->ca = ca; } return h; } /** Free HashTab */ static void hashtab_destroy(struct HashTab *h) { struct HashTab *tmp; while (h) { tmp = h->next; cx_free(h->ca, h); h = tmp; } } /** Element lookup, optionally inserting new slot */ static htab_val_t *hashtab_lookup(struct HashTab *h, htab_key_t key, bool do_insert, const void *arg) { unsigned pos; struct HashItem *i; loop: /* find key, starting from pos */ pos = CALC_POS(h, key); while (h->tab[pos].value) { i = &h->tab[pos]; HTAB_RMB; if (i->key == key) { if (arg && h->cmp_fn(i->value, arg)) return &i->value; } pos = NEXT_POS(h, pos); } /* not found in this one, check chained tables */ if (h->next) { h = h->next; goto loop; } /* just lookup? */ if (!do_insert) return NULL; /* insert */ if (h->used >= MAX_USED(h)) { struct HashTab *tmp; tmp = hashtab_create(h->size, h->cmp_fn, h->ca); if (!tmp) return NULL; h->next = tmp; h = tmp; pos = CALC_POS(h, key); } h->used++; h->tab[pos].key = key; HTAB_WMB; return &h->tab[pos].value; } /* if proper pos is between src and dst, cannot move */ static bool _hashtab_slot_can_move(struct HashTab *h, unsigned dstpos, unsigned srcpos) { htab_key_t key = h->tab[srcpos].key; unsigned pos, kpos = CALC_POS(h, key); if (kpos == srcpos) return false; if (kpos == dstpos) return true; for (pos = NEXT_POS(h, dstpos); pos != srcpos; pos = NEXT_POS(h, pos)) { if (pos == kpos) return false; } return true; } /** Delete an element */ static void hashtab_delete(struct HashTab *h, htab_key_t key, void *arg) { htab_val_t *vptr; struct HashItem *hd; unsigned pos, dstpos; /* find it */ vptr = hashtab_lookup(h, key, false, arg); if (!vptr) return; /* find right tab */ hd = container_of(vptr, struct HashItem, value); while (h && ((hd < h->tab) || (hd >= h->tab + h->size))) h = h->next; /* calculate index */ dstpos = hd - h->tab; loop: /* move slot */ for (pos = NEXT_POS(h, dstpos); h->tab[pos].value; pos = NEXT_POS(h, pos)) { if (_hashtab_slot_can_move(h, dstpos, pos)) { h->tab[dstpos].key = h->tab[pos].key; h->tab[dstpos].value = h->tab[pos].value; dstpos = pos; goto loop; } } h->tab[dstpos].value = 0; HTAB_WMB; h->tab[dstpos].key = 0; h->used--; } /** Count elements and fragments */ static void hashtab_stats(struct HashTab *h, unsigned *nitem_p, unsigned *ntab_p) { unsigned n = 0, l = 0; while (h) { l++; n += h->used; h = h->next; } *nitem_p = n; *ntab_p = l; } /** Copy elements to new hashtab, perhaps with different size */ static struct HashTab *hashtab_copy(struct HashTab *h_old, unsigned newsize) { struct HashTab *h_new; unsigned i; h_new = hashtab_create(newsize, h_old->cmp_fn, h_old->ca); for (; h_old; h_old = h_old->next) { for (i = 0; i < h_old->size; i++) { struct HashItem *s = &h_old->tab[i]; htab_val_t *new_pos; if (s->value) { new_pos = hashtab_lookup(h_new, s->key, true, NULL); if (!new_pos) goto err; *new_pos = s->value; } } } return h_new; err: hashtab_destroy(h_new); return NULL; } /* example, and avoid "unused" warnings */ static inline void _hashtab_example(void) { unsigned nitem, nlink; struct HashTab *h, *h2; h = hashtab_create(1024, NULL, NULL); hashtab_lookup(h, 123, true, NULL); hashtab_stats(h, &nitem, &nlink); h2 = hashtab_copy(h, 2048); hashtab_delete(h, 123, NULL); hashtab_destroy(h); hashtab_destroy(h2); } pgbouncer-1.7/lib/usual/pgutil_kwlookup.h0000664000175000017500000005374312511202014015577 00000000000000/* ANSI-C code produced by gperf version 3.0.3 */ /* Command-line: gperf -m5 usual/pgutil_kwlookup.g */ /* Computed positions: -k'1-2,6,9,$' */ #if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \ && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \ && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \ && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \ && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \ && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \ && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) /* The character set is not based on ISO-646. */ #error "gperf generated tables don't work with this execution character set. Please report a bug to ." #endif /* maximum key range = 296, duplicates = 0 */ #ifdef __GNUC__ __inline #else #ifdef __cplusplus inline #endif #endif static unsigned int pg_keyword_lookup_hash (register const char *str, register unsigned int len) { static const unsigned short asso_values[] = { 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 38, 125, 31, 64, 10, 96, 60, 125, 26, 7, 5, 13, 63, 10, 12, 70, 312, 5, 19, 3, 71, 131, 65, 50, 77, 3, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312 }; register int hval = len; switch (hval) { default: hval += asso_values[(unsigned char)str[8]]; /*FALLTHROUGH*/ case 8: case 7: case 6: hval += asso_values[(unsigned char)str[5]]; /*FALLTHROUGH*/ case 5: case 4: case 3: case 2: hval += asso_values[(unsigned char)str[1]]; /*FALLTHROUGH*/ case 1: hval += asso_values[(unsigned char)str[0]]; break; } return hval + asso_values[(unsigned char)str[len - 1]]; } #ifdef __GNUC__ __inline #ifdef __GNUC_STDC_INLINE__ __attribute__ ((__gnu_inline__)) #endif #endif const char * pg_keyword_lookup_real (register const char *str, register unsigned int len) { enum { TOTAL_KEYWORDS = 148, MIN_WORD_LENGTH = 2, MAX_WORD_LENGTH = 17, MIN_HASH_VALUE = 16, MAX_HASH_VALUE = 311 }; struct pgkw_t { char pgkw_str16[sizeof("treat")]; char pgkw_str22[sizeof("true")]; char pgkw_str24[sizeof("or")]; char pgkw_str27[sizeof("order")]; char pgkw_str28[sizeof("not")]; char pgkw_str29[sizeof("to")]; char pgkw_str30[sizeof("left")]; char pgkw_str31[sizeof("least")]; char pgkw_str32[sizeof("real")]; char pgkw_str33[sizeof("join")]; char pgkw_str34[sizeof("on")]; char pgkw_str36[sizeof("none")]; char pgkw_str37[sizeof("else")]; char pgkw_str39[sizeof("right")]; char pgkw_str41[sizeof("select")]; char pgkw_str42[sizeof("int")]; char pgkw_str43[sizeof("time")]; char pgkw_str44[sizeof("inout")]; char pgkw_str45[sizeof("some")]; char pgkw_str46[sizeof("inner")]; char pgkw_str47[sizeof("limit")]; char pgkw_str48[sizeof("in")]; char pgkw_str51[sizeof("nchar")]; char pgkw_str52[sizeof("into")]; char pgkw_str53[sizeof("like")]; char pgkw_str54[sizeof("ilike")]; char pgkw_str55[sizeof("notnull")]; char pgkw_str56[sizeof("table")]; char pgkw_str57[sizeof("localtime")]; char pgkw_str58[sizeof("integer")]; char pgkw_str60[sizeof("cross")]; char pgkw_str62[sizeof("create")]; char pgkw_str63[sizeof("collate")]; char pgkw_str64[sizeof("references")]; char pgkw_str66[sizeof("is")]; char pgkw_str67[sizeof("all")]; char pgkw_str68[sizeof("analyze")]; char pgkw_str69[sizeof("column")]; char pgkw_str70[sizeof("intersect")]; char pgkw_str71[sizeof("constraint")]; char pgkw_str72[sizeof("except")]; char pgkw_str73[sizeof("grant")]; char pgkw_str75[sizeof("trim")]; char pgkw_str76[sizeof("cast")]; char pgkw_str77[sizeof("isnull")]; char pgkw_str78[sizeof("as")]; char pgkw_str79[sizeof("national")]; char pgkw_str80[sizeof("coalesce")]; char pgkw_str83[sizeof("case")]; char pgkw_str84[sizeof("analyse")]; char pgkw_str85[sizeof("row")]; char pgkw_str86[sizeof("greatest")]; char pgkw_str87[sizeof("end")]; char pgkw_str88[sizeof("new")]; char pgkw_str89[sizeof("out")]; char pgkw_str90[sizeof("do")]; char pgkw_str91[sizeof("asc")]; char pgkw_str92[sizeof("old")]; char pgkw_str93[sizeof("outer")]; char pgkw_str95[sizeof("similar")]; char pgkw_str96[sizeof("union")]; char pgkw_str97[sizeof("default")]; char pgkw_str98[sizeof("null")]; char pgkw_str99[sizeof("user")]; char pgkw_str100[sizeof("leading")]; char pgkw_str101[sizeof("extract")]; char pgkw_str102[sizeof("trailing")]; char pgkw_str103[sizeof("only")]; char pgkw_str104[sizeof("exists")]; char pgkw_str106[sizeof("natural")]; char pgkw_str107[sizeof("unique")]; char pgkw_str108[sizeof("dec")]; char pgkw_str109[sizeof("desc")]; char pgkw_str111[sizeof("distinct")]; char pgkw_str112[sizeof("deferrable")]; char pgkw_str115[sizeof("and")]; char pgkw_str116[sizeof("for")]; char pgkw_str117[sizeof("float")]; char pgkw_str119[sizeof("smallint")]; char pgkw_str120[sizeof("offset")]; char pgkw_str122[sizeof("localtimestamp")]; char pgkw_str123[sizeof("precision")]; char pgkw_str125[sizeof("array")]; char pgkw_str126[sizeof("position")]; char pgkw_str127[sizeof("freeze")]; char pgkw_str128[sizeof("any")]; char pgkw_str129[sizeof("session_user")]; char pgkw_str130[sizeof("setof")]; char pgkw_str132[sizeof("decimal")]; char pgkw_str133[sizeof("xmlforest")]; char pgkw_str134[sizeof("asymmetric")]; char pgkw_str135[sizeof("xmlroot")]; char pgkw_str136[sizeof("xmlparse")]; char pgkw_str137[sizeof("current_time")]; char pgkw_str138[sizeof("xmlconcat")]; char pgkw_str139[sizeof("current_role")]; char pgkw_str140[sizeof("group")]; char pgkw_str142[sizeof("then")]; char pgkw_str144[sizeof("xmlpi")]; char pgkw_str145[sizeof("numeric")]; char pgkw_str146[sizeof("xmlelement")]; char pgkw_str147[sizeof("concurrently")]; char pgkw_str149[sizeof("false")]; char pgkw_str152[sizeof("over")]; char pgkw_str153[sizeof("xmlserialize")]; char pgkw_str154[sizeof("returning")]; char pgkw_str155[sizeof("using")]; char pgkw_str157[sizeof("bit")]; char pgkw_str160[sizeof("placing")]; char pgkw_str162[sizeof("between")]; char pgkw_str163[sizeof("bigint")]; char pgkw_str164[sizeof("primary")]; char pgkw_str165[sizeof("char")]; char pgkw_str166[sizeof("check")]; char pgkw_str168[sizeof("from")]; char pgkw_str170[sizeof("symmetric")]; char pgkw_str175[sizeof("authorization")]; char pgkw_str177[sizeof("verbose")]; char pgkw_str181[sizeof("timestamp")]; char pgkw_str183[sizeof("current_schema")]; char pgkw_str184[sizeof("full")]; char pgkw_str185[sizeof("foreign")]; char pgkw_str186[sizeof("xmlexists")]; char pgkw_str188[sizeof("interval")]; char pgkw_str192[sizeof("boolean")]; char pgkw_str198[sizeof("current_date")]; char pgkw_str200[sizeof("current_user")]; char pgkw_str202[sizeof("current_timestamp")]; char pgkw_str204[sizeof("when")]; char pgkw_str205[sizeof("where")]; char pgkw_str206[sizeof("character")]; char pgkw_str207[sizeof("off")]; char pgkw_str208[sizeof("overlaps")]; char pgkw_str213[sizeof("values")]; char pgkw_str218[sizeof("current_catalog")]; char pgkw_str219[sizeof("varchar")]; char pgkw_str220[sizeof("with")]; char pgkw_str224[sizeof("substring")]; char pgkw_str227[sizeof("window")]; char pgkw_str236[sizeof("fetch")]; char pgkw_str237[sizeof("initially")]; char pgkw_str265[sizeof("overlay")]; char pgkw_str266[sizeof("both")]; char pgkw_str272[sizeof("variadic")]; char pgkw_str273[sizeof("xmlattributes")]; char pgkw_str279[sizeof("nullif")]; char pgkw_str289[sizeof("having")]; char pgkw_str311[sizeof("binary")]; }; static const struct pgkw_t pgkw_contents = { "treat", "true", "or", "order", "not", "to", "left", "least", "real", "join", "on", "none", "else", "right", "select", "int", "time", "inout", "some", "inner", "limit", "in", "nchar", "into", "like", "ilike", "notnull", "table", "localtime", "integer", "cross", "create", "collate", "references", "is", "all", "analyze", "column", "intersect", "constraint", "except", "grant", "trim", "cast", "isnull", "as", "national", "coalesce", "case", "analyse", "row", "greatest", "end", "new", "out", "do", "asc", "old", "outer", "similar", "union", "default", "null", "user", "leading", "extract", "trailing", "only", "exists", "natural", "unique", "dec", "desc", "distinct", "deferrable", "and", "for", "float", "smallint", "offset", "localtimestamp", "precision", "array", "position", "freeze", "any", "session_user", "setof", "decimal", "xmlforest", "asymmetric", "xmlroot", "xmlparse", "current_time", "xmlconcat", "current_role", "group", "then", "xmlpi", "numeric", "xmlelement", "concurrently", "false", "over", "xmlserialize", "returning", "using", "bit", "placing", "between", "bigint", "primary", "char", "check", "from", "symmetric", "authorization", "verbose", "timestamp", "current_schema", "full", "foreign", "xmlexists", "interval", "boolean", "current_date", "current_user", "current_timestamp", "when", "where", "character", "off", "overlaps", "values", "current_catalog", "varchar", "with", "substring", "window", "fetch", "initially", "overlay", "both", "variadic", "xmlattributes", "nullif", "having", "binary" }; #define pgkw ((const char *) &pgkw_contents) static const int wordlist[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str16, -1, -1, -1, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str22, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str24, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str27, (int)(long)&((struct pgkw_t *)0)->pgkw_str28, (int)(long)&((struct pgkw_t *)0)->pgkw_str29, (int)(long)&((struct pgkw_t *)0)->pgkw_str30, (int)(long)&((struct pgkw_t *)0)->pgkw_str31, (int)(long)&((struct pgkw_t *)0)->pgkw_str32, (int)(long)&((struct pgkw_t *)0)->pgkw_str33, (int)(long)&((struct pgkw_t *)0)->pgkw_str34, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str36, (int)(long)&((struct pgkw_t *)0)->pgkw_str37, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str39, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str41, (int)(long)&((struct pgkw_t *)0)->pgkw_str42, (int)(long)&((struct pgkw_t *)0)->pgkw_str43, (int)(long)&((struct pgkw_t *)0)->pgkw_str44, (int)(long)&((struct pgkw_t *)0)->pgkw_str45, (int)(long)&((struct pgkw_t *)0)->pgkw_str46, (int)(long)&((struct pgkw_t *)0)->pgkw_str47, (int)(long)&((struct pgkw_t *)0)->pgkw_str48, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str51, (int)(long)&((struct pgkw_t *)0)->pgkw_str52, (int)(long)&((struct pgkw_t *)0)->pgkw_str53, (int)(long)&((struct pgkw_t *)0)->pgkw_str54, (int)(long)&((struct pgkw_t *)0)->pgkw_str55, (int)(long)&((struct pgkw_t *)0)->pgkw_str56, (int)(long)&((struct pgkw_t *)0)->pgkw_str57, (int)(long)&((struct pgkw_t *)0)->pgkw_str58, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str60, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str62, (int)(long)&((struct pgkw_t *)0)->pgkw_str63, (int)(long)&((struct pgkw_t *)0)->pgkw_str64, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str66, (int)(long)&((struct pgkw_t *)0)->pgkw_str67, (int)(long)&((struct pgkw_t *)0)->pgkw_str68, (int)(long)&((struct pgkw_t *)0)->pgkw_str69, (int)(long)&((struct pgkw_t *)0)->pgkw_str70, (int)(long)&((struct pgkw_t *)0)->pgkw_str71, (int)(long)&((struct pgkw_t *)0)->pgkw_str72, (int)(long)&((struct pgkw_t *)0)->pgkw_str73, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str75, (int)(long)&((struct pgkw_t *)0)->pgkw_str76, (int)(long)&((struct pgkw_t *)0)->pgkw_str77, (int)(long)&((struct pgkw_t *)0)->pgkw_str78, (int)(long)&((struct pgkw_t *)0)->pgkw_str79, (int)(long)&((struct pgkw_t *)0)->pgkw_str80, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str83, (int)(long)&((struct pgkw_t *)0)->pgkw_str84, (int)(long)&((struct pgkw_t *)0)->pgkw_str85, (int)(long)&((struct pgkw_t *)0)->pgkw_str86, (int)(long)&((struct pgkw_t *)0)->pgkw_str87, (int)(long)&((struct pgkw_t *)0)->pgkw_str88, (int)(long)&((struct pgkw_t *)0)->pgkw_str89, (int)(long)&((struct pgkw_t *)0)->pgkw_str90, (int)(long)&((struct pgkw_t *)0)->pgkw_str91, (int)(long)&((struct pgkw_t *)0)->pgkw_str92, (int)(long)&((struct pgkw_t *)0)->pgkw_str93, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str95, (int)(long)&((struct pgkw_t *)0)->pgkw_str96, (int)(long)&((struct pgkw_t *)0)->pgkw_str97, (int)(long)&((struct pgkw_t *)0)->pgkw_str98, (int)(long)&((struct pgkw_t *)0)->pgkw_str99, (int)(long)&((struct pgkw_t *)0)->pgkw_str100, (int)(long)&((struct pgkw_t *)0)->pgkw_str101, (int)(long)&((struct pgkw_t *)0)->pgkw_str102, (int)(long)&((struct pgkw_t *)0)->pgkw_str103, (int)(long)&((struct pgkw_t *)0)->pgkw_str104, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str106, (int)(long)&((struct pgkw_t *)0)->pgkw_str107, (int)(long)&((struct pgkw_t *)0)->pgkw_str108, (int)(long)&((struct pgkw_t *)0)->pgkw_str109, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str111, (int)(long)&((struct pgkw_t *)0)->pgkw_str112, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str115, (int)(long)&((struct pgkw_t *)0)->pgkw_str116, (int)(long)&((struct pgkw_t *)0)->pgkw_str117, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str119, (int)(long)&((struct pgkw_t *)0)->pgkw_str120, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str122, (int)(long)&((struct pgkw_t *)0)->pgkw_str123, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str125, (int)(long)&((struct pgkw_t *)0)->pgkw_str126, (int)(long)&((struct pgkw_t *)0)->pgkw_str127, (int)(long)&((struct pgkw_t *)0)->pgkw_str128, (int)(long)&((struct pgkw_t *)0)->pgkw_str129, (int)(long)&((struct pgkw_t *)0)->pgkw_str130, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str132, (int)(long)&((struct pgkw_t *)0)->pgkw_str133, (int)(long)&((struct pgkw_t *)0)->pgkw_str134, (int)(long)&((struct pgkw_t *)0)->pgkw_str135, (int)(long)&((struct pgkw_t *)0)->pgkw_str136, (int)(long)&((struct pgkw_t *)0)->pgkw_str137, (int)(long)&((struct pgkw_t *)0)->pgkw_str138, (int)(long)&((struct pgkw_t *)0)->pgkw_str139, (int)(long)&((struct pgkw_t *)0)->pgkw_str140, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str142, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str144, (int)(long)&((struct pgkw_t *)0)->pgkw_str145, (int)(long)&((struct pgkw_t *)0)->pgkw_str146, (int)(long)&((struct pgkw_t *)0)->pgkw_str147, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str149, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str152, (int)(long)&((struct pgkw_t *)0)->pgkw_str153, (int)(long)&((struct pgkw_t *)0)->pgkw_str154, (int)(long)&((struct pgkw_t *)0)->pgkw_str155, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str157, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str160, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str162, (int)(long)&((struct pgkw_t *)0)->pgkw_str163, (int)(long)&((struct pgkw_t *)0)->pgkw_str164, (int)(long)&((struct pgkw_t *)0)->pgkw_str165, (int)(long)&((struct pgkw_t *)0)->pgkw_str166, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str168, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str170, -1, -1, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str175, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str177, -1, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str181, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str183, (int)(long)&((struct pgkw_t *)0)->pgkw_str184, (int)(long)&((struct pgkw_t *)0)->pgkw_str185, (int)(long)&((struct pgkw_t *)0)->pgkw_str186, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str188, -1, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str192, -1, -1, -1, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str198, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str200, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str202, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str204, (int)(long)&((struct pgkw_t *)0)->pgkw_str205, (int)(long)&((struct pgkw_t *)0)->pgkw_str206, (int)(long)&((struct pgkw_t *)0)->pgkw_str207, (int)(long)&((struct pgkw_t *)0)->pgkw_str208, -1, -1, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str213, -1, -1, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str218, (int)(long)&((struct pgkw_t *)0)->pgkw_str219, (int)(long)&((struct pgkw_t *)0)->pgkw_str220, -1, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str224, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str227, -1, -1, -1, -1, -1, -1, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str236, (int)(long)&((struct pgkw_t *)0)->pgkw_str237, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str265, (int)(long)&((struct pgkw_t *)0)->pgkw_str266, -1, -1, -1, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str272, (int)(long)&((struct pgkw_t *)0)->pgkw_str273, -1, -1, -1, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str279, -1, -1, -1, -1, -1, -1, -1, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str289, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, (int)(long)&((struct pgkw_t *)0)->pgkw_str311 }; if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) { register int key = pg_keyword_lookup_hash (str, len); if (key <= MAX_HASH_VALUE && key >= 0) { register int o = wordlist[key]; if (o >= 0) { register const char *s = o + pgkw; if (*str == *s && !strcmp (str + 1, s + 1)) return s; } } } return 0; } pgbouncer-1.7/lib/usual/signal.h0000664000175000017500000000503412511202014013603 00000000000000/* * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * Signals compat. * * general * - sigaction() -> signal() * * win32: * - SIGALRM, alarm(), signal(SIGALRM), sigaction(SIGALRM) * - kill(pid, 0) */ #ifndef _USUAL_SIGNAL_H_ #define _USUAL_SIGNAL_H_ #include #include /* * Compat sigval, detect based on siginfo_t.si_code. */ #if !defined(SI_QUEUE) && !defined(HAVE_SIGQUEUE) union sigval { int sival_int; void *sival_ptr; }; #endif /* * Compat sigevent */ #ifndef SIGEV_NONE #define SIGEV_NONE 0 #define SIGEV_SIGNAL 1 #define SIGEV_THREAD 2 struct sigevent { int sigev_notify; int sigev_signo; union sigval sigev_value; void (*sigev_notify_function)(union sigval); }; #endif /* * Compat sigaction() */ #ifndef HAVE_SIGACTION #define SA_SIGINFO 1 #define SA_RESTART 2 typedef struct siginfo_t siginfo_t; struct sigaction { union { void (*sa_handler)(int); void (*sa_sigaction)(int, siginfo_t *, void *); }; int sa_flags; int sa_mask; }; #define sigemptyset(s) #define sigfillset(s) #define sigaddset(s, sig) #define sigdelset(s, sig) #define sigaction(a,b,c) compat_sigaction(a,b,c) int sigaction(int sig, const struct sigaction *sa, struct sigaction *old); #endif /* * win32 compat: * kill(), alarm, SIGALRM */ #ifdef WIN32 #define SIGALRM 1023 #define SIGBUS 1022 unsigned alarm(unsigned); int kill(int pid, int sig); typedef void (*_sighandler_t)(int); static inline _sighandler_t wrap_signal(int sig, _sighandler_t func) { /* sigaction has custom handling for SIGALRM */ if (sig == SIGALRM) { struct sigaction sa, oldsa; sa.sa_handler = func; sa.sa_flags = sa.sa_mask = 0; sigaction(SIGALRM, &sa, &oldsa); return oldsa.sa_handler; } else if (sig == SIGBUS) { return NULL; } return signal(sig, func); } #define signal(a,b) wrap_signal(a,b) #endif #endif pgbouncer-1.7/lib/usual/bytemap.h0000664000175000017500000000637412511203511014002 00000000000000/* * byte map * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Map 256 byte values to bit or int. */ #ifndef _USUAL_BYTEMAP_H_ #define _USUAL_BYTEMAP_H_ #define BITMAP256_SHIFT 5 #define BITMAP256_MASK ((1 << BITMAP256_SHIFT) - 1) /** * Bitmap of 256 bits. */ struct Bitmap256 { uint32_t bmap[256 / 32]; }; /** * Clear bitmap. */ static inline void bitmap256_init(struct Bitmap256 *bmap) { memset(bmap, 0, sizeof(*bmap)); } /** * Set one bit. */ static inline void bitmap256_set(struct Bitmap256 *bmap, uint8_t byte) { bmap->bmap[byte >> BITMAP256_SHIFT] |= 1 << (byte & BITMAP256_MASK); } /** * Check if bit is set. */ static inline bool bitmap256_is_set(const struct Bitmap256 *bmap, uint8_t byte) { return bmap->bmap[byte >> BITMAP256_SHIFT] & (1 << (byte & BITMAP256_MASK)); } /* * Declare const value of bytemap */ /** * Use C preprocessor to fill Bitmap256. * * Usage: * @code * #define check_isdigit(c) ((c) >= '0' && (c) <= '9') * static const struct Bitmap256 map_isdigit = BITMAP256_CONST(check_isdigit); * @endcode */ #define BITMAP256_CONST(check) {{ \ _BMAP256_V32(check,0), _BMAP256_V32(check,32), _BMAP256_V32(check,64), _BMAP256_V32(check,96), \ _BMAP256_V32(check,128), _BMAP256_V32(check,160), _BMAP256_V32(check,192), _BMAP256_V32(check,224) }} #define _BMAP256_V32(ck,p) \ _BMAP256_V8(ck,(p)+0) | _BMAP256_V8(ck,(p)+8) | _BMAP256_V8(ck,(p)+16) | _BMAP256_V8(ck,(p)+24) #define _BMAP256_V8(ck,p) \ _BMAP256_BIT(ck,(p)+0) | _BMAP256_BIT(ck,(p)+1) | _BMAP256_BIT(ck,(p)+2) | _BMAP256_BIT(ck,(p)+3) | \ _BMAP256_BIT(ck,(p)+4) | _BMAP256_BIT(ck,(p)+5) | _BMAP256_BIT(ck,(p)+6) | _BMAP256_BIT(ck,(p)+7) #define _BMAP256_BIT(ck,p) (ck(p) ? (1 << ((p) & BMAP256_MASK)) : 0) /** * Use C preprocessor to generate array of 256 values. * * Usage: * @code * #define my_hexval(c) (((c) >= '0' && (c) <= '9') ? ((c) - '0') : ( \ * ((c) >= 'A' && (c) <= 'F') ? ((c) - 'A' + 10) : ( \ * ((c) >= 'a' && (c) <= 'f') ? ((c) - 'a' + 10) : -1 ))) * static const int map_hexval[] = INTMAP256_CONST(my_hexval); * @endcode */ #define INTMAP256_CONST(map_value) { _INTMAP_V128(map_value,0), _INTMAP_V128(map_value,128) } #define _INTMAP_V128(mf,n) _INTMAP_V32(mf,(n)+0*32), _INTMAP_V32(mf,(n)+1*32), _INTMAP_V32(mf,(n)+2*32), _INTMAP_V32(mf,(n)+3*32) #define _INTMAP_V32(mf,n) _INTMAP_V8(mf,(n)+0*8), _INTMAP_V8(mf,(n)+1*8), _INTMAP_V8(mf,(n)+2*8), _INTMAP_V8(mf,(n)+3*8) #define _INTMAP_V8(mf,n) mf((n)+0), mf((n)+1), mf((n)+2), mf((n)+3), mf((n)+4), mf((n)+5), mf((n)+6), mf((n)+7) #endif pgbouncer-1.7/lib/usual/time.h0000664000175000017500000000513012576101475013305 00000000000000/* * Theme include for time. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Time-related functionality. */ #ifndef _USUAL_TIME_H_ #define _USUAL_TIME_H_ #include #ifdef HAVE_SYS_TIME_H #include #endif #ifdef _WIN32 #include #endif #include /** Type to hold microseconds. */ typedef uint64_t usec_t; /** How many microseconds in a second. */ #define USEC ((usec_t)1000000) /** Convert usec timestamp to ISO timestamp with millisecond precision: YYYY-mm-dd hh:mm:ss.SSS */ char *format_time_ms(usec_t time, char *dst, unsigned dstlen); /** Convert usec timestamp to ISO timestamp with second precision: YYYY-mm-dd hh:mm:ss */ char *format_time_s(usec_t time, char *dst, unsigned dstlen); /** Query system time */ usec_t get_time_usec(void); /** Query cached system time */ usec_t get_cached_time(void); /** Forget cached system time, next call will fill it. */ void reset_time_cache(void); #ifdef WIN32 #ifndef HAVE_GETTIMEOFDAY #define gettimeofday(t,z) usual_gettimeofday(t,z) /** Compat: gettimeofday() */ int gettimeofday(struct timeval * tp, void * tzp); #endif #ifndef HAVE_LOCALTIME_R #define localtime_r(t,b) usual_localtime_r(t,b) /** Compat: localtime_r() */ struct tm *localtime_r(const time_t *tp, struct tm *buf); #endif #ifndef HAVE_TIMEGM #define timegm(tm) usual_timegm(tm) /** Compat: timegm() */ time_t timegm(struct tm *tm); #endif #ifndef HAVE_USLEEP #define usleep(x) usual_usleep(x) /** Compat: usleep() */ static inline void usleep(long usec) { Sleep(usec / 1000); } #endif #ifndef HAVE_GETRUSAGE #define getrusage(w,d) usual_getrusage(w,d) #define RUSAGE_SELF 0 /** Compat: rusage for win32 */ struct rusage { struct timeval ru_utime; struct timeval ru_stime; }; /** Compat: getrusage() for win32 */ int getrusage(int who, struct rusage *dst); #endif #endif #endif pgbouncer-1.7/lib/usual/shlist.h0000664000175000017500000001046312511203511013641 00000000000000/* * Circular list for shared mem. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Circular list for shared mem. * * Instead of pointers, it uses offsets from list head. */ #ifndef _USUAL_SHLIST_H_ #define _USUAL_SHLIST_H_ #include /* clang: pointers are hard */ #if defined(__clang__) #define __shlist_clang_workaround__ volatile #else #define __shlist_clang_workaround__ #endif /** List node/head. Uses offsets from current node instead of direct pointers. */ struct SHList { ptrdiff_t next; ptrdiff_t prev; }; /* * Calculate offset relative to base. * * Instead of using some third pointer (eg. shmem start) as base, * we use node itself as base. This results in simpler APi * and also means that empty node appears as zero-filled. */ /** Get next element in list */ static inline struct SHList *shlist_get_next(const struct SHList *node) { char *p = (char *)node + node->next; return (struct SHList *)p; } /** Get prev element in list */ static inline struct SHList *shlist_get_prev(const struct SHList *node) { char *p = (char *)node + node->prev; return (struct SHList *)p; } static inline void _shlist_set_next(__shlist_clang_workaround__ struct SHList *node, const struct SHList *next) { node->next = (char *)next - (char *)node; } static inline void _shlist_set_prev(__shlist_clang_workaround__ struct SHList *node, const struct SHList *prev) { node->prev = (char *)prev - (char *)node; } /* * List operations. */ /** Initialize list head */ static inline void shlist_init(struct SHList *list) { list->next = 0; list->prev = 0; } /** Insert as last element */ static inline void shlist_append(struct SHList *list, struct SHList *node) { struct SHList *last; last = shlist_get_prev(list); _shlist_set_next(node, list); _shlist_set_prev(node, last); _shlist_set_next(last, node); _shlist_set_prev(list, node); } /** Insert as first element */ static inline void shlist_prepend(struct SHList *list, struct SHList *node) { struct SHList *first; first = shlist_get_next(list); _shlist_set_next(node, first); _shlist_set_prev(node, list); _shlist_set_next(list, node); _shlist_set_prev(first, node); } /** Remove an node */ static inline void shlist_remove(struct SHList *node) { struct SHList *next = shlist_get_next(node); struct SHList *prev = shlist_get_prev(node); _shlist_set_prev(next, prev); _shlist_set_next(prev, next); shlist_init(node); } /** No elements? */ static inline bool shlist_empty(const struct SHList *list) { return list->next == 0; } /** Return first elem */ static inline struct SHList *shlist_first(const struct SHList *list) { if (shlist_empty(list)) return NULL; return shlist_get_next(list); } /** Return last elem */ static inline struct SHList *shlist_last(const struct SHList *list) { if (shlist_empty(list)) return NULL; return shlist_get_prev(list); } /** Remove first elem */ static inline struct SHList *shlist_pop(struct SHList *list) { struct SHList *node = shlist_first(list); if (node) shlist_remove(node); return node; } /** Remove and return specific type of elem */ #define shlist_pop_type(list, type, field) ( \ shlist_empty(list) ? NULL : container_of(shlist_pop(list), type, field)) /** Loop over list */ #define shlist_for_each(node, list) \ for ((node) = shlist_get_next(list); \ (node) != (list); \ (node) = shlist_get_next(node)) /** Loop over list and allow removing node */ #define shlist_for_each_safe(node, list, tmp) \ for ((node) = shlist_get_next(list), (tmp) = shlist_get_next(node); \ (node) != (list); \ (node) = (tmp), (tmp) = shlist_get_next(node)) #endif pgbouncer-1.7/lib/usual/dlfcn.c0000664000175000017500000000240612511203511013412 00000000000000/* * Dynamic library loading. * * Copyright (c) 2007-2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifdef _WIN32 #include /* * win32: Minimal dlopen, dlsym, dlclose, dlerror compat. */ void *dlopen(const char *fn, int flag) { HMODULE h = LoadLibraryEx(fn, NULL, 0); return h; } void *dlsym(void *hptr, const char *fname) { HMODULE h = hptr; FARPROC f = GetProcAddress(h, fname); return f; } int dlclose(void *hptr) { HMODULE h = hptr; return FreeLibrary(h) ? 0 : -1; } const char *dlerror(void) { return strerror(GetLastError()); } #endif pgbouncer-1.7/lib/usual/config.h.in0000664000175000017500000003135512615704033014222 00000000000000/* lib/usual/config.h.in. Generated from configure.ac by autoheader. */ /* Define if building universal (internal helper macro) */ #undef AC_APPLE_UNIVERSAL_BUILD /* Define to enable assert checking */ #undef CASSERT /* Define to 1 if you have the `arc4random_buf' function. */ #undef HAVE_ARC4RANDOM_BUF /* Define to 1 if you have the `ares_parse_soa_reply' function. */ #undef HAVE_ARES_PARSE_SOA_REPLY /* Define to 1 if you have the header file. */ #undef HAVE_ARPA_INET_H /* Define to 1 if you have the `asn1_time_parse' function. */ #undef HAVE_ASN1_TIME_PARSE /* Define to 1 if you have the `asprintf' function. */ #undef HAVE_ASPRINTF /* Define to 1 if you have the header file. */ #undef HAVE_BYTESWAP_H /* Define to 1 if you have the declaration of `strerror_r', and to 0 if you don't. */ #undef HAVE_DECL_STRERROR_R /* Define to 1 if you have the header file. */ #undef HAVE_DLFCN_H /* Define if *enc & *dec functions are available */ #undef HAVE_ENCDEC_FUNCS /* Define to 1 if you have the header file. */ #undef HAVE_ENDIAN_H /* Define to 1 if you have the `err' function. */ #undef HAVE_ERR /* Define to 1 if you have the `errx' function. */ #undef HAVE_ERRX /* Define to 1 if you have the header file. */ #undef HAVE_ERR_H /* Define to 1 if you have the `evdns_base_new' function. */ #undef HAVE_EVDNS_BASE_NEW /* Define to 1 if you have the `event_base_new' function. */ #undef HAVE_EVENT_BASE_NEW /* Define to 1 if you have the `event_loopbreak' function. */ #undef HAVE_EVENT_LOOPBREAK /* Define to 1 if you have the `explicit_bzero' function. */ #undef HAVE_EXPLICIT_BZERO /* Define to 1 if you have the `ffs' function. */ #undef HAVE_FFS /* Define to 1 if you have the `ffsl' function. */ #undef HAVE_FFSL /* Define to 1 if you have the `ffsll' function. */ #undef HAVE_FFSLL /* Define to 1 if you have the `fls' function. */ #undef HAVE_FLS /* Define to 1 if you have the `flsl' function. */ #undef HAVE_FLSL /* Define to 1 if you have the `flsll' function. */ #undef HAVE_FLSLL /* Define to 1 if you have the `fnmatch' function. */ #undef HAVE_FNMATCH /* Define to 1 if you have the header file. */ #undef HAVE_FNMATCH_H /* Define to 1 if your compiler understands __func__. */ #undef HAVE_FUNCNAME__FUNC /* Define to 1 if you have the getaddrinfo_a() function. */ #undef HAVE_GETADDRINFO_A /* Define to 1 if you have the `getentropy' function. */ #undef HAVE_GETENTROPY /* Define to 1 if you have the `getline' function. */ #undef HAVE_GETLINE /* Define to 1 if you have the `getopt' function. */ #undef HAVE_GETOPT /* Define to 1 if you have the header file. */ #undef HAVE_GETOPT_H /* Define to 1 if you have the `getopt_long' function. */ #undef HAVE_GETOPT_LONG /* Define to 1 if you have the `getopt_long_only' function. */ #undef HAVE_GETOPT_LONG_ONLY /* Define to 1 if you have the `getpeereid' function. */ #undef HAVE_GETPEEREID /* Define to 1 if you have the `getpeerucred' function. */ #undef HAVE_GETPEERUCRED /* Define to 1 if you have the `getprogname' function. */ #undef HAVE_GETPROGNAME /* Define to 1 if you have the `getrusage' function. */ #undef HAVE_GETRUSAGE /* Define to 1 if you have the `gettimeofday' function. */ #undef HAVE_GETTIMEOFDAY /* Define to 1 if you have the header file. */ #undef HAVE_GRP_H /* Define to 1 if you have the `inet_ntop' function. */ #undef HAVE_INET_NTOP /* Define to 1 if you have the `inet_pton' function. */ #undef HAVE_INET_PTON /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H /* Define to 1 if you have the header file. */ #undef HAVE_LANGINFO_H /* Use real libevent. */ #undef HAVE_LIBEVENT /* Define to 1 if you have the `localtime_r' function. */ #undef HAVE_LOCALTIME_R /* Define to 1 if you have the `lstat' function. */ #undef HAVE_LSTAT /* Define to 1 if you have the header file. */ #undef HAVE_MALLOC_H /* Define to 1 if you have the `mbsnrtowcs' function. */ #undef HAVE_MBSNRTOWCS /* Define to 1 if you have the `memalign' function. */ #undef HAVE_MEMALIGN /* Define to 1 if you have the `memmem' function. */ #undef HAVE_MEMMEM /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H /* Define to 1 if you have the `mempcpy' function. */ #undef HAVE_MEMPCPY /* Define to 1 if you have the `memrchr' function. */ #undef HAVE_MEMRCHR /* Define to 1 if you have the `memset_s' function. */ #undef HAVE_MEMSET_S /* Define to 1 if you have the `mmap' function. */ #undef HAVE_MMAP /* Define to 1 if you have the header file. */ #undef HAVE_NETDB_H /* Define to 1 if you have the header file. */ #undef HAVE_NETINET_IN_H /* Define to 1 if you have the header file. */ #undef HAVE_NETINET_TCP_H /* Define to 1 if you have the `nl_langinfo' function. */ #undef HAVE_NL_LANGINFO /* Define to 1 if you have the `poll' function. */ #undef HAVE_POLL /* Define to 1 if you have the header file. */ #undef HAVE_POLL_H /* Define to 1 if you have the `posix_memalign' function. */ #undef HAVE_POSIX_MEMALIGN /* Define if you have POSIX threads libraries and header files. */ #undef HAVE_PTHREAD /* Define to 1 if you have the header file. */ #undef HAVE_PTHREAD_H /* Define to 1 if you have the header file. */ #undef HAVE_PWD_H /* Define to 1 if you have the `reallocarray' function. */ #undef HAVE_REALLOCARRAY /* Define to 1 if you have the `recvmsg' function. */ #undef HAVE_RECVMSG /* Define to 1 if you have the `regcomp' function. */ #undef HAVE_REGCOMP /* Define to 1 if you have the header file. */ #undef HAVE_REGEX_H /* Define to 1 if you have the `sendmsg' function. */ #undef HAVE_SENDMSG /* Define to 1 if you have the `setprogname' function. */ #undef HAVE_SETPROGNAME /* Define to 1 if you have the `sigaction' function. */ #undef HAVE_SIGACTION /* Define to 1 if you have the `sigqueue' function. */ #undef HAVE_SIGQUEUE /* Define to 1 if you have the `SSL_CTX_load_verify_mem' function. */ #undef HAVE_SSL_CTX_LOAD_VERIFY_MEM /* Define to 1 if you have the `SSL_CTX_use_certificate_chain_mem' function. */ #undef HAVE_SSL_CTX_USE_CERTIFICATE_CHAIN_MEM /* Define to 1 if you have the header file. */ #undef HAVE_STDBOOL_H /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H /* Define to 1 if you have the `strerror_r' function. */ #undef HAVE_STRERROR_R /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H /* Define to 1 if you have the `strlcat' function. */ #undef HAVE_STRLCAT /* Define to 1 if you have the `strlcpy' function. */ #undef HAVE_STRLCPY /* Define to 1 if you have the `strsep' function. */ #undef HAVE_STRSEP /* Define to 1 if you have the `strtod_l' function. */ #undef HAVE_STRTOD_L /* Define to 1 if you have the `strtonum' function. */ #undef HAVE_STRTONUM /* Define to 1 if you have the `syslog' function. */ #undef HAVE_SYSLOG /* Define to 1 if you have the header file. */ #undef HAVE_SYSLOG_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_ENDIAN_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_MMAN_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_PARAM_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_POLL_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_RESOURCE_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_SOCKET_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TIME_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TYPES_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_UCRED_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_UIO_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_UN_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_WAIT_H /* Define to 1 if you have the `timegm' function. */ #undef HAVE_TIMEGM /* Define to 1 if you have the header file. */ #undef HAVE_UCRED_H /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H /* Define to 1 if you have the `usleep' function. */ #undef HAVE_USLEEP /* Define to 1 if you have the `valloc' function. */ #undef HAVE_VALLOC /* Define to 1 if you have the `vasprintf' function. */ #undef HAVE_VASPRINTF /* Define to 1 if you have the `warn' function. */ #undef HAVE_WARN /* Define to 1 if you have the `warnx' function. */ #undef HAVE_WARNX /* Define to 1 if you have the header file. */ #undef HAVE_XLOCALE_H /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ #undef PACKAGE_NAME /* Define to the full name and version of this package. */ #undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME /* Define to the home page for this package. */ #undef PACKAGE_URL /* Define to the version of this package. */ #undef PACKAGE_VERSION /* Define to necessary symbol if this constant uses a non-standard name on your system. */ #undef PTHREAD_CREATE_JOINABLE /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS /* Define to 1 if strerror_r returns char *. */ #undef STRERROR_R_CHAR_P /* Use c-ares for name resolution. */ #undef USE_CARES /* Use libevent for DNS lookups. */ #undef USE_EVDNS /* Use UDNS for name resolution. */ #undef USE_UDNS /* Use libssl for TLS. */ #undef USUAL_LIBSSL_FOR_TLS /* Path to root CA certs. */ #undef USUAL_TLS_CA_FILE /* Define to request cleaner win32 headers. */ #undef WIN32_LEAN_AND_MEAN /* Define to max win32 API version (0x0501=XP). */ #undef WINVER /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel). */ #if defined AC_APPLE_UNIVERSAL_BUILD # if defined __BIG_ENDIAN__ # define WORDS_BIGENDIAN 1 # endif #else # ifndef WORDS_BIGENDIAN # undef WORDS_BIGENDIAN # endif #endif /* Number of bits in a file offset, on hosts where this is settable. */ #undef _FILE_OFFSET_BITS /* Define to get working glibc. */ #undef _GNU_SOURCE /* Define for large files, on AIX-style hosts. */ #undef _LARGE_FILES /* Define for Solaris 2.5.1 so the uint32_t typedef from , , or is not used. If the typedef were allowed, the #define below would cause a syntax error. */ #undef _UINT32_T /* Define for Solaris 2.5.1 so the uint64_t typedef from , , or is not used. If the typedef were allowed, the #define below would cause a syntax error. */ #undef _UINT64_T /* Define for Solaris 2.5.1 so the uint8_t typedef from , , or is not used. If the typedef were allowed, the #define below would cause a syntax error. */ #undef _UINT8_T /* Define to `int' if doesn't define. */ #undef gid_t /* Define to `__inline__' or `__inline' if that's what the C compiler calls it, or to nothing if 'inline' is not supported under any name. */ #ifndef __cplusplus #undef inline #endif /* Define to `int' if does not define. */ #undef pid_t /* Define to the equivalent of the C99 'restrict' keyword, or to nothing if this is not supported. Do not define if restrict is supported directly. */ #undef restrict /* Work around a bug in Sun C++: it does not support _Restrict or __restrict__, even though the corresponding Sun C compiler ends up with "#define restrict _Restrict" or "#define restrict __restrict__" in the previous line. Perhaps some future version of Sun C++ will work with restrict; if so, hopefully it defines __RESTRICT like Sun C does. */ #if defined __SUNPRO_CC && !defined __RESTRICT # define _Restrict # define __restrict__ #endif /* Define to `unsigned int' if does not define. */ #undef size_t /* Define to `int' if doesn't define. */ #undef uid_t /* Define to the type of an unsigned integer type of width exactly 32 bits if such a type exists and the standard includes do not define it. */ #undef uint32_t /* Define to the type of an unsigned integer type of width exactly 64 bits if such a type exists and the standard includes do not define it. */ #undef uint64_t /* Define to the type of an unsigned integer type of width exactly 8 bits if such a type exists and the standard includes do not define it. */ #undef uint8_t pgbouncer-1.7/lib/usual/dlfcn.h0000664000175000017500000000254212511203511013420 00000000000000/* * Dynamic library loading. * * Copyright (c) 2007-2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _USUAL_DLFCN_H_ #define _USUAL_DLFCN_H_ #ifdef HAVE_DLFCN_H #include #elif defined(_WIN32) #define dlopen(a,b) usual_dlopen(a,b) #define dlsym(a,b) usual_dlsym(a,b) #define dlclose(a) usual_dlclose(a) #define dlerror(...) usual_dlerror(__VA_ARGS__) /* * win32: Minimal dlopen, dlsym, dlclose, dlerror compat. */ #define RTLD_LAZY 1 #define RTLD_NOW 2 void *dlopen(const char *fn, int flag); void *dlsym(void *hptr, const char *fname); int dlclose(void *hptr); const char *dlerror(void); #endif /* _WIN32 */ #endif /* !_USUAL_DLFCN_H_ */ pgbouncer-1.7/lib/usual/hashing/0000775000175000017500000000000012635051616013675 500000000000000pgbouncer-1.7/lib/usual/hashing/siphash.c0000664000175000017500000000500012567562476015432 00000000000000/* * Copyright (c) 2012 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #define SIP_ROUND1 \ v0 += v1; v1 = rol64(v1, 13); v1 ^= v0; v0 = rol64(v0, 32); \ v2 += v3; v3 = rol64(v3, 16); v3 ^= v2; \ v0 += v3; v3 = rol64(v3, 21); v3 ^= v0; \ v2 += v1; v1 = rol64(v1, 17); v1 ^= v2; v2 = rol64(v2, 32) #define SIP_ROUND2 SIP_ROUND1; SIP_ROUND1 #define SIP_ROUND4 SIP_ROUND2; SIP_ROUND2 #define SIP_ROUNDS(n) SIP_ROUND ## n #define sip_compress(n) \ do { \ v3 ^= m; \ SIP_ROUNDS(n); \ v0 ^= m; \ } while (0) #define sip_finalize(n) \ do { \ v2 ^= 0xff; \ SIP_ROUNDS(n); \ } while (0) uint64_t siphash24(const void *data, size_t len, uint64_t k0, uint64_t k1) { const uint8_t *s = data; const uint8_t *end = s + len - (len % 8); uint64_t v0 = k0 ^ UINT64_C(0x736f6d6570736575); uint64_t v1 = k1 ^ UINT64_C(0x646f72616e646f6d); uint64_t v2 = k0 ^ UINT64_C(0x6c7967656e657261); uint64_t v3 = k1 ^ UINT64_C(0x7465646279746573); uint64_t m; for (; s < end; s += 8) { m = le64dec(s); sip_compress(2); } m = (uint64_t)len << 56; switch (len & 7) { case 7: m |= (uint64_t)s[6] << 48; case 6: m |= (uint64_t)s[5] << 40; case 5: m |= (uint64_t)s[4] << 32; case 4: m |= (uint64_t)s[3] << 24; case 3: m |= (uint64_t)s[2] << 16; case 2: m |= (uint64_t)s[1] << 8; case 1: m |= (uint64_t)s[0]; break; case 0: break; } sip_compress(2); sip_finalize(4); return (v0 ^ v1 ^ v2 ^ v3); } uint64_t siphash24_secure(const void *data, size_t len) { static bool initialized; static uint64_t k0, k1; if (!initialized) { k0 = ((uint64_t)csrandom() << 32) | csrandom(); k1 = ((uint64_t)csrandom() << 32) | csrandom(); initialized = true; } return siphash24(data, len, k0, k1); } pgbouncer-1.7/lib/usual/hashing/xxhash.h0000664000175000017500000000355712556647420015311 00000000000000/* xxHash - Fast Hash algorithm Header File Copyright (C) 2012-2014, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - xxHash source repository : http://code.google.com/p/xxhash/ */ /** * @file * * xxHash - fast hash for 32-bit cpu's. */ #ifndef _USUAL_HASHING_XXHASH_H_ #define _USUAL_HASHING_XXHASH_H_ #include /** * Calculate the 32-bits hash of sequence of length "len" stored at memory address "input". */ uint32_t xxhash(const void *input, size_t len, uint32_t seed); #endif pgbouncer-1.7/lib/usual/hashing/lookup3.c0000664000175000017500000000270612511203511015344 00000000000000/* * The contents of this file are public domain. * * Based on: lookup3.c, by Bob Jenkins, May 2006, Public Domain. */ /* * Compact version of Bob Jenkins' lookup3.c hash. */ #include #include #define rot(x, k) (((x)<<(k)) | ((x)>>(32-(k)))) #define mix(a, b, c) do { \ a -= c; a ^= rot(c, 4); c += b; \ b -= a; b ^= rot(a, 6); a += c; \ c -= b; c ^= rot(b, 8); b += a; \ a -= c; a ^= rot(c,16); c += b; \ b -= a; b ^= rot(a,19); a += c; \ c -= b; c ^= rot(b, 4); b += a; \ } while (0) #define final(a, b, c) do { \ c ^= b; c -= rot(b,14); \ a ^= c; a -= rot(c,11); \ b ^= a; b -= rot(a,25); \ c ^= b; c -= rot(b,16); \ a ^= c; a -= rot(c, 4); \ b ^= a; b -= rot(a,14); \ c ^= b; c -= rot(b,24); \ } while (0) /* variable length copy of ~6 bytes, avoid call to libc */ static inline void simple_memcpy(void *dst_, const void *src_, size_t len) { const uint8_t *src = src_; uint8_t *dst = dst_; while (len--) *dst++ = *src++; } uint64_t hash_lookup3(const void *data, size_t len) { uint32_t a, b, c; uint32_t buf[3]; const uint8_t *p = data; a = b = c = 0xdeadbeef + len; if (len == 0) goto done; while (len > 12) { memcpy(buf, p, 12); a += buf[0]; b += buf[1]; c += buf[2]; mix(a, b, c); p += 12; len -= 12; } buf[0] = buf[1] = buf[2] = 0; simple_memcpy(buf, p, len); a += buf[0]; b += buf[1]; c += buf[2]; final(a, b, c); done: return ((uint64_t)b << 32) | c; } pgbouncer-1.7/lib/usual/hashing/memhash.c0000664000175000017500000000303612556647420015413 00000000000000/* * memhash.h - Randomized in-memory hashing. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include uint32_t memhash_seed(const void *data, size_t len, uint32_t seed) { if (sizeof(void *) == 8 || sizeof(long) == 8) { uint64_t hash[2]; hash[0] = seed; hash[1] = 0; spookyhash(data, len, &hash[0], &hash[1]); return hash[0]; } else { return xxhash(data, len, seed); } } uint32_t memhash(const void *data, size_t len) { static bool initialized; static uint32_t rand_seed; if (!initialized) { initialized = true; rand_seed = csrandom(); } return memhash_seed(data, len, rand_seed); } uint32_t memhash_string(const char *s) { return memhash(s, strlen(s)); } pgbouncer-1.7/lib/usual/hashing/lookup3.h0000664000175000017500000000040612511203511015344 00000000000000/** * @file * * Jenkins' lookup3 non-cryptographic hash. */ #ifndef _USUAL_HASHING_LOOKUP3_H_ #define _USUAL_HASHING_LOOKUP3_H_ #include /** * Calculate 64-bit hash over data */ uint64_t hash_lookup3(const void *data, size_t len); #endif pgbouncer-1.7/lib/usual/hashing/siphash.h0000664000175000017500000000207512511203511015413 00000000000000/* * Copyright (c) 2012 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * SipHash-2-4 */ #ifndef _USUAL_HASHING_SIPHASH_H_ #define _USUAL_HASHING_SIPHASH_H_ #include /** Calculate SipHash-2-4 checksum */ uint64_t siphash24(const void *data, size_t len, uint64_t k0, uint64_t k1); uint64_t siphash24_secure(const void *data, size_t len); #endif pgbouncer-1.7/lib/usual/hashing/memhash.h0000664000175000017500000000255112556647420015421 00000000000000/* * memhash.h - Randomized in-memory hashing. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Randomized in-memory hashing. * * Functions here use randomized seed and pick fastest hash * for current CPU. */ #ifndef _USUAL_HASHING_MEMHASH_H_ #define _USUAL_HASHING_MEMHASH_H_ #include /** * Hash data. */ uint32_t memhash(const void *data, size_t len); /** * Hash zero-terminated string. */ uint32_t memhash_string(const char *s); /** * Hash with given seed. Result is not randomized, * but still may vary on different CPU-s. */ uint32_t memhash_seed(const void *data, size_t len, uint32_t seed); #endif pgbouncer-1.7/lib/usual/hashing/spooky.h0000664000175000017500000000045112556647420015320 00000000000000/** * @file * * Jenkins SpookyHash V2 - fast hash for 64-bit CPUs. */ #ifndef _USUAL_HASHING_SPOOKY_H_ #define _USUAL_HASHING_SPOOKY_H_ #include /** * Run SpookyHash on data. */ void spookyhash(const void *message, size_t length, uint64_t *hash1, uint64_t *hash2); #endif pgbouncer-1.7/lib/usual/hashing/xxhash.c0000664000175000017500000000547312511203511015257 00000000000000/* xxHash - Fast Hash algorithm Copyright (C) 2012-2014, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - xxHash source repository : http://code.google.com/p/xxhash/ */ #include #include #include #define PRIME32_1 2654435761U #define PRIME32_2 2246822519U #define PRIME32_3 3266489917U #define PRIME32_4 668265263U #define PRIME32_5 374761393U #define read32(p) h32dec(p) uint32_t xxhash(const void *input, size_t len, uint32_t seed) { const uint8_t *p = (const uint8_t *)input; const uint8_t * const bEnd = p + len; uint32_t h32; if (len >= 16) { const uint8_t * const limit = bEnd - 16; uint32_t v1, v2, v3, v4; v1 = seed + PRIME32_1 + PRIME32_2; v2 = seed + PRIME32_2; v3 = seed + 0; v4 = seed - PRIME32_1; do { v1 += read32(p) * PRIME32_2; v1 = rol32(v1, 13); v1 *= PRIME32_1; p += 4; v2 += read32(p) * PRIME32_2; v2 = rol32(v2, 13); v2 *= PRIME32_1; p += 4; v3 += read32(p) * PRIME32_2; v3 = rol32(v3, 13); v3 *= PRIME32_1; p += 4; v4 += read32(p) * PRIME32_2; v4 = rol32(v4, 13); v4 *= PRIME32_1; p += 4; } while (p <= limit); h32 = rol32(v1, 1) + rol32(v2, 7) + rol32(v3, 12) + rol32(v4, 18); } else { h32 = seed + PRIME32_5; } h32 += len; while (p <= bEnd - 4) { h32 += read32(p) * PRIME32_3; h32 = rol32(h32, 17) * PRIME32_4 ; p += 4; } while (p < bEnd) { h32 += (*p) * PRIME32_5; h32 = rol32(h32, 11) * PRIME32_1 ; p++; } h32 ^= h32 >> 15; h32 *= PRIME32_2; h32 ^= h32 >> 13; h32 *= PRIME32_3; h32 ^= h32 >> 16; return h32; } pgbouncer-1.7/lib/usual/hashing/crc32.h0000664000175000017500000000176312511203511014673 00000000000000/* * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * CRC32 checksum. */ #ifndef _USUAL_HASHING_CRC32_H_ #define _USUAL_HASHING_CRC32_H_ #include /** Calculate CRC32 checksum */ uint32_t calc_crc32(const void *data, size_t len, uint32_t init); #endif pgbouncer-1.7/lib/usual/hashing/spooky.c0000664000175000017500000002504112511203511015271 00000000000000/* * SpookyHash: a 128-bit noncryptographic hash function * By Bob Jenkins, public domain * Oct 31 2010: alpha, framework + SpookyHash::Mix appears right * Oct 31 2011: alpha again, Mix only good to 2^^69 but rest appears right * Dec 31 2011: beta, improved Mix, tested it for 2-bit deltas * Feb 2 2012: production, same bits as beta * Feb 5 2012: adjusted definitions of uint* to be more portable * Mar 30 2012: 3 bytes/cycle, not 4. Alpha was 4 but wasn't thorough enough. * August 5 2012: SpookyV2 (different results) * * Up to 3 bytes/cycle for long messages. Reasonably fast for short messages. * All 1 or 2 bit deltas achieve avalanche within 1% bias per output bit. * * This was developed for and tested on 64-bit x86-compatible processors. * It assumes the processor is little-endian. There is a macro * controlling whether unaligned reads are allowed (by default they are). * This should be an equally good hash on big-endian machines, but it will * compute different results on them than on little-endian machines. * * Google's CityHash has similar specs to SpookyHash, and CityHash is faster * on new Intel boxes. MD4 and MD5 also have similar specs, but they are orders * of magnitude slower. CRCs are two or more times slower, but unlike * SpookyHash, they have nice math for combining the CRCs of pieces to form * the CRCs of wholes. There are also cryptographic hashes, but those are even * slower than MD5. */ #include #include #include #ifdef WORDS_UNALIGNED_ACCESS_OK #define ALLOW_UNALIGNED_READS 1 #else #define ALLOW_UNALIGNED_READS 0 #endif /* number of uint64_t's in internal state */ #define sc_numVars 12 /* size of the internal state */ #define sc_blockSize (sc_numVars*8) /* size of buffer of unhashed data, in bytes */ #define sc_bufSize (2*sc_blockSize) /* * sc_const: a constant which: * - is not zero * - is odd * - is a not-very-regular mix of 1's and 0's * - does not need any other special mathematical properties */ static const uint64_t sc_const = 0xdeadbeefdeadbeefLL; /* * This is used if the input is 96 bytes long or longer. * * The internal state is fully overwritten every 96 bytes. * Every input bit appears to cause at least 128 bits of entropy * before 96 other bytes are combined, when run forward or backward * For every input bit, * Two inputs differing in just that input bit * Where "differ" means xor or subtraction * And the base value is random * When run forward or backwards one Mix * I tried 3 pairs of each; they all differed by at least 212 bits. */ #define Mix(data, s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11) \ do { \ s0 += data[0]; s2 ^= s10; s11 ^= s0; s0 = rol64(s0,11); s11 += s1; \ s1 += data[1]; s3 ^= s11; s0 ^= s1; s1 = rol64(s1,32); s0 += s2; \ s2 += data[2]; s4 ^= s0; s1 ^= s2; s2 = rol64(s2,43); s1 += s3; \ s3 += data[3]; s5 ^= s1; s2 ^= s3; s3 = rol64(s3,31); s2 += s4; \ s4 += data[4]; s6 ^= s2; s3 ^= s4; s4 = rol64(s4,17); s3 += s5; \ s5 += data[5]; s7 ^= s3; s4 ^= s5; s5 = rol64(s5,28); s4 += s6; \ s6 += data[6]; s8 ^= s4; s5 ^= s6; s6 = rol64(s6,39); s5 += s7; \ s7 += data[7]; s9 ^= s5; s6 ^= s7; s7 = rol64(s7,57); s6 += s8; \ s8 += data[8]; s10 ^= s6; s7 ^= s8; s8 = rol64(s8,55); s7 += s9; \ s9 += data[9]; s11 ^= s7; s8 ^= s9; s9 = rol64(s9,54); s8 += s10; \ s10 += data[10]; s0 ^= s8; s9 ^= s10; s10 = rol64(s10,22); s9 += s11; \ s11 += data[11]; s1 ^= s9; s10 ^= s11; s11 = rol64(s11,46); s10 += s0; \ } while (0) /* * Mix all 12 inputs together so that h0, h1 are a hash of them all. * * For two inputs differing in just the input bits * Where "differ" means xor or subtraction * And the base value is random, or a counting value starting at that bit * The final result will have each bit of h0, h1 flip * For every input bit, * with probability 50 +- .3% * For every pair of input bits, * with probability 50 +- 3% * * This does not rely on the last Mix() call having already mixed some. * Two iterations was almost good enough for a 64-bit result, but a * 128-bit result is reported, so End() does three iterations. */ #define EndPartial(h0, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10, h11) \ do { \ h11+= h1; h2 ^= h11; h1 = rol64(h1,44); \ h0 += h2; h3 ^= h0; h2 = rol64(h2,15); \ h1 += h3; h4 ^= h1; h3 = rol64(h3,34); \ h2 += h4; h5 ^= h2; h4 = rol64(h4,21); \ h3 += h5; h6 ^= h3; h5 = rol64(h5,38); \ h4 += h6; h7 ^= h4; h6 = rol64(h6,33); \ h5 += h7; h8 ^= h5; h7 = rol64(h7,10); \ h6 += h8; h9 ^= h6; h8 = rol64(h8,13); \ h7 += h9; h10^= h7; h9 = rol64(h9,38); \ h8 += h10; h11^= h8; h10= rol64(h10,53); \ h9 += h11; h0 ^= h9; h11= rol64(h11,42); \ h10+= h0; h1 ^= h10; h0 = rol64(h0,54); \ } while (0) #define End(data, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10, h11) \ do { \ h0 += data[0]; h1 += data[1]; h2 += data[2]; h3 += data[3]; \ h4 += data[4]; h5 += data[5]; h6 += data[6]; h7 += data[7]; \ h8 += data[8]; h9 += data[9]; h10 += data[10]; h11 += data[11]; \ EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); \ EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); \ EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); \ } while (0) /* * The goal is for each bit of the input to expand into 128 bits of * apparent entropy before it is fully overwritten. * n trials both set and cleared at least m bits of h0 h1 h2 h3 * n: 2 m: 29 * n: 3 m: 46 * n: 4 m: 57 * n: 5 m: 107 * n: 6 m: 146 * n: 7 m: 152 * when run forwards or backwards * for all 1-bit and 2-bit diffs * with diffs defined by either xor or subtraction * with a base of all zeros plus a counter, or plus another bit, or random */ #define ShortMix(h0, h1, h2, h3) \ do { \ h2 = rol64(h2,50); h2 += h3; h0 ^= h2; \ h3 = rol64(h3,52); h3 += h0; h1 ^= h3; \ h0 = rol64(h0,30); h0 += h1; h2 ^= h0; \ h1 = rol64(h1,41); h1 += h2; h3 ^= h1; \ h2 = rol64(h2,54); h2 += h3; h0 ^= h2; \ h3 = rol64(h3,48); h3 += h0; h1 ^= h3; \ h0 = rol64(h0,38); h0 += h1; h2 ^= h0; \ h1 = rol64(h1,37); h1 += h2; h3 ^= h1; \ h2 = rol64(h2,62); h2 += h3; h0 ^= h2; \ h3 = rol64(h3,34); h3 += h0; h1 ^= h3; \ h0 = rol64(h0,5); h0 += h1; h2 ^= h0; \ h1 = rol64(h1,36); h1 += h2; h3 ^= h1; \ } while (0) /* * Mix all 4 inputs together so that h0, h1 are a hash of them all. * * For two inputs differing in just the input bits * Where "differ" means xor or subtraction * And the base value is random, or a counting value starting at that bit * The final result will have each bit of h0, h1 flip * For every input bit, * with probability 50 +- .3% (it is probably better than that) * For every pair of input bits, * with probability 50 +- .75% (the worst case is approximately that) */ #define ShortEnd(h0, h1, h2, h3) \ do { \ h3 ^= h2; h2 = rol64(h2,15); h3 += h2; \ h0 ^= h3; h3 = rol64(h3,52); h0 += h3; \ h1 ^= h0; h0 = rol64(h0,26); h1 += h0; \ h2 ^= h1; h1 = rol64(h1,51); h2 += h1; \ h3 ^= h2; h2 = rol64(h2,28); h3 += h2; \ h0 ^= h3; h3 = rol64(h3,9); h0 += h3; \ h1 ^= h0; h0 = rol64(h0,47); h1 += h0; \ h2 ^= h1; h1 = rol64(h1,54); h2 += h1; \ h3 ^= h2; h2 = rol64(h2,32); h3 += h2; \ h0 ^= h3; h3 = rol64(h3,25); h0 += h3; \ h1 ^= h0; h0 = rol64(h0,63); h1 += h0; \ } while (0) /* * Short is used for messages under 192 bytes in length * Short has a low startup cost, the normal mode is good for long * keys, the cost crossover is at about 192 bytes. The two modes were * held to the same quality bar. */ static void Short(const void *message, size_t length, uint64_t *hash1, uint64_t *hash2) { uint64_t buf[2*sc_numVars]; union { const uint8_t *p8; uint32_t *p32; uint64_t *p64; size_t i; } u; size_t remainder = length%32; uint64_t a=*hash1; uint64_t b=*hash2; uint64_t c=sc_const; uint64_t d=sc_const; u.p8 = (const uint8_t *)message; if (!ALLOW_UNALIGNED_READS && (u.i & 0x7)) { memcpy(buf, message, length); u.p64 = buf; } if (length > 15) { const uint64_t *end = u.p64 + (length/32)*4; /* handle all complete sets of 32 bytes */ for (; u.p64 < end; u.p64 += 4) { c += u.p64[0]; d += u.p64[1]; ShortMix(a,b,c,d); a += u.p64[2]; b += u.p64[3]; } /* Handle the case of 16+ remaining bytes. */ if (remainder >= 16) { c += u.p64[0]; d += u.p64[1]; ShortMix(a,b,c,d); u.p64 += 2; remainder -= 16; } } /* Handle the last 0..15 bytes, and its length */ d += ((uint64_t)length) << 56; switch (remainder) { case 15: d += ((uint64_t)u.p8[14]) << 48; case 14: d += ((uint64_t)u.p8[13]) << 40; case 13: d += ((uint64_t)u.p8[12]) << 32; case 12: d += u.p32[2]; c += u.p64[0]; break; case 11: d += ((uint64_t)u.p8[10]) << 16; case 10: d += ((uint64_t)u.p8[9]) << 8; case 9: d += (uint64_t)u.p8[8]; case 8: c += u.p64[0]; break; case 7: c += ((uint64_t)u.p8[6]) << 48; case 6: c += ((uint64_t)u.p8[5]) << 40; case 5: c += ((uint64_t)u.p8[4]) << 32; case 4: c += u.p32[0]; break; case 3: c += ((uint64_t)u.p8[2]) << 16; case 2: c += ((uint64_t)u.p8[1]) << 8; case 1: c += (uint64_t)u.p8[0]; break; case 0: c += sc_const; d += sc_const; } ShortEnd(a,b,c,d); *hash1 = a; *hash2 = b; } /* do the whole hash in one call */ void spookyhash(const void *message, size_t length, uint64_t *hash1, uint64_t *hash2) { uint64_t h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11; uint64_t buf[sc_numVars]; uint64_t *end; union { const uint8_t *p8; uint64_t *p64; } u; size_t remainder; if (length < sc_bufSize) { Short(message, length, hash1, hash2); return; } h0 = h3 = h6 = h9 = *hash1; h1 = h4 = h7 = h10 = *hash2; h2 = h5 = h8 = h11 = sc_const; u.p8 = (const uint8_t *)message; end = u.p64 + (length/sc_blockSize)*sc_numVars; /* handle all whole sc_blockSize blocks of bytes */ if (ALLOW_UNALIGNED_READS || (((uintptr_t)u.p8 & 0x7) == 0)) { while (u.p64 < end) { Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); u.p64 += sc_numVars; } } else { while (u.p64 < end) { memcpy(buf, u.p64, sc_blockSize); Mix(buf, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); u.p64 += sc_numVars; } } /* handle the last partial block of sc_blockSize bytes */ remainder = (length - ((const uint8_t *)end-(const uint8_t *)message)); memcpy(buf, end, remainder); memset(((uint8_t *)buf)+remainder, 0, sc_blockSize-remainder); ((uint8_t *)buf)[sc_blockSize-1] = remainder; /* do some final mixing */ End(buf, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); *hash1 = h0; *hash2 = h1; } pgbouncer-1.7/lib/usual/hashing/crc32.c0000664000175000017500000001021112511203511014652 00000000000000/* * CRC32. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include static const uint32_t crc_tab[256] = { 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D }; static inline uint32_t crc32(uint32_t prev, uint8_t c) { return crc_tab[(prev ^ c) & 0xFF] ^ (prev >> 8); } uint32_t calc_crc32(const void *data, size_t len, uint32_t init) { const uint8_t *p = data; uint32_t crc = init ^ (~0); while (len--) crc = crc32(crc, *p++); return crc ^ (~0); } pgbouncer-1.7/lib/usual/string.h0000664000175000017500000001410512615704017013652 00000000000000/* * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * \file * Theme include for strings. */ #ifndef _USUAL_STRING_H_ #define _USUAL_STRING_H_ #include #include /** * @name List of strings. * @{ */ /** Callback signature */ typedef bool (*str_cb)(void *arg, const char *s); struct StrList; /** Allocate new string list */ struct StrList *strlist_new(CxMem *ca); /** Free string string */ void strlist_free(struct StrList *slist); /** Check if empty */ bool strlist_empty(struct StrList *slist); /** Append copy of string. */ bool strlist_append(struct StrList *slist, const char *str); /** Append reference, strlist now owns it. */ bool strlist_append_ref(struct StrList *slist, const char *str); /** Call function on each element */ bool strlist_foreach(const struct StrList *slist, str_cb cb_func, void *cb_arg); /** Remove and return first element */ const char *strlist_pop(struct StrList *slist); /* @} */ /** Parse comma-separated elements from string and launch callback for each of them. */ bool parse_word_list(const char *s, str_cb cb_func, void *cb_arg); #ifndef HAVE_STRLCPY #define strlcpy(a,b,c) usual_strlcpy(a,b,c) /** Compat: Safely copy string to fixed-length buffer. */ size_t strlcpy(char *dst, const char *src, size_t n); #endif #ifndef HAVE_STRLCAT #define strlcat(a,b,c) usual_strlcat(a,b,c) /** Compat: Safely append string to fixed-length buffer. */ size_t strlcat(char *dst, const char *src, size_t n); #endif #undef strpcpy #define strpcpy(a,b,c) usual_strpcpy(a,b,c) /** * Safe string copy. * * Returns pointer to end of string in dst or NULL * if truncation occured. Destination will be * zero-terminated unless dstlen is 0. */ char *strpcpy(char *dst, const char *src, size_t dstlen); #undef strpcat #define strpcat(a,b,c) usual_strpcat(a,b,c) /** * Safe string concat. * * Returns pointer to end of string in dst or NULL if truncation occured. * Destination will be zero-terminated, unless dstlen is 0 or existing * contents were not zero-terminated. */ char *strpcat(char *dst, const char *src, size_t dstlen); #ifndef HAVE_MEMRCHR #define memrchr(a,b,c) usual_memrchr(a,b,c) /** Compat: find byte in reverse direction */ void *memrchr(const void *s, int c, size_t n); #endif #ifndef HAVE_MEMMEM #define memmem(a,b,c,d) usual_memmem(a,b,c,d) /** Compat: find memory area */ void *memmem(const void *s, size_t slen, const void *q, size_t qlen); #endif #ifndef HAVE_MEMPCPY #define mempcpy(a,b,c) usual_mempcpy(a,b,c) /** Copy memory, return pointer to end. */ void *mempcpy(void *dst, const void *src, size_t len); #endif /** Return position to first byte that is in 'find'. */ void *mempbrk(const void *data, size_t dlen, const void *find, size_t flen); /** Return number of bytes where none are in reject. */ size_t memcspn(const void *data, size_t dlen, const void *reject, size_t rlen); /** Return number of bytes where all are in accept. */ size_t memspn(const void *data, size_t dlen, const void *accept, size_t alen); #ifndef HAVE_BASENAME #undef basename #define basename(a) usual_basename(a) /** Compat: Return pointer to last non-path element. Never modifies path, returns either pointer inside path or static buffer. */ const char *basename(const char *path); #endif #ifndef HAVE_DIRNAME #undef dirname #define dirname(a) usual_dirname(a) /** Compat: Return directory part of pathname. Never modifies path, returns either pointer inside path or static buffer. */ const char *dirname(const char *path); #endif #ifndef HAVE_EXPLICIT_BZERO #undef explicit_bzero #define explicit_bzero(a,b) usual_explicit_bzero(a,b) /** Definitely clear memory */ void explicit_bzero(void *buf, size_t len); #endif /* * strerror, strerror_r */ #ifdef WIN32 const char *win32_strerror(int e); /** Compat: strerror() for win32 */ #define strerror(x) win32_strerror(x) #endif const char *usual_strerror_r(int e, char *dst, size_t dstlen); /** Compat: Provide GNU-style API: const char *strerror_r(int e, char *dst, size_t dstlen) */ #define strerror_r(a,b,c) usual_strerror_r(a,b,c) /** strtod() that uses '.' as decimal separator */ double strtod_dot(const char *s, char **tokend); /** Convert double to string with '.' as decimal separator */ ssize_t dtostr_dot(char *buf, size_t buflen, double val); #ifndef HAVE_STRTONUM #define strtonum(a,b,c,d) usual_strtonum(a,b,c,d) /** * Convert string to integer, check limits. * * Accepts only decimal numbers, no octal or hex. Allows leading whitespace, * but not tailing. * * On success, returns value that is minval <= res <= maxval. If errstr_p is given * it stores NULL there. Keeps old errno value. * * On error, returns 0, sets errno, and if errstr_p is given, stores error reason there. */ long long strtonum(const char *s, long long minval, long long maxval, const char **errstr_p); #endif #ifndef HAVE_STRSEP #undef strsep #define strsep(a,b) usual_strsep(a,b) /** * Return next token from string. * * Tokens are separated by delim chars * Modifies string in-place. */ char *strsep(char **stringp, const char *delim); #endif #ifndef HAVE_ASPRINTF #define asprintf(dst_p, fmt, ...) usual_asprintf(dst_p, fmt, __VA_ARGS__) int asprintf(char **dst_p, const char *fmt, ...) _PRINTF(2, 3); #endif #ifndef HAVE_VASPRINTF #define vasprintf(dst_p, fmt, ap) usual_vasprintf(dst_p, fmt, ap) int vasprintf(char **dst_p, const char *fmt, va_list ap) _PRINTF(2, 0); #endif #endif pgbouncer-1.7/lib/usual/slab.c0000664000175000017500000001471712511203511013255 00000000000000/* * Primitive slab allocator. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #ifndef USUAL_FAKE_SLAB /* * Store for pre-initialized objects of one type. */ struct Slab { struct List head; struct StatList freelist; struct StatList fraglist; char name[32]; unsigned final_size; unsigned total_count; slab_init_fn init_func; CxMem *cx; }; /* * Header for each slab. */ struct SlabFrag { struct List head; }; /* keep track of all active slabs */ static STATLIST(slab_list); static void slab_list_append(struct Slab *slab) { #ifndef _REENTRANT statlist_append(&slab_list, &slab->head); #endif } static void slab_list_remove(struct Slab *slab) { #ifndef _REENTRANT statlist_remove(&slab_list, &slab->head); #endif } /* fill struct contents */ static void init_slab(struct Slab *slab, const char *name, unsigned obj_size, unsigned align, slab_init_fn init_func, CxMem *cx) { unsigned slen = strlen(name); list_init(&slab->head); statlist_init(&slab->freelist, name); statlist_init(&slab->fraglist, name); slab->total_count = 0; slab->init_func = init_func; slab->cx = cx; if (slen >= sizeof(slab->name)) slen = sizeof(slab->name) - 1; memcpy(slab->name, name, slen); slab->name[slen] = 0; /* don't allow too small align, as we want to put pointers into area */ if (align < sizeof(long)) align = 0; /* actual area for one object */ if (align == 0) slab->final_size = ALIGN(obj_size); else slab->final_size = CUSTOM_ALIGN(obj_size, align); /* allow small structs */ if (slab->final_size < sizeof(struct List)) slab->final_size = sizeof(struct List); slab_list_append(slab); } /* make new slab */ struct Slab *slab_create(const char *name, unsigned obj_size, unsigned align, slab_init_fn init_func, CxMem *cx) { struct Slab *slab; /* new slab object */ slab = cx_alloc0(cx, sizeof(*slab)); if (slab) init_slab(slab, name, obj_size, align, init_func, cx); return slab; } /* free all storage associated by slab */ void slab_destroy(struct Slab *slab) { struct List *item, *tmp; struct SlabFrag *frag; if (!slab) return; slab_list_remove(slab); statlist_for_each_safe(item, &slab->fraglist, tmp) { frag = container_of(item, struct SlabFrag, head); cx_free(slab->cx, frag); } cx_free(slab->cx, slab); } /* add new block of objects to slab */ static void grow(struct Slab *slab) { unsigned count, i, size; char *area; struct SlabFrag *frag; /* calc new slab size */ count = slab->total_count; if (count < 50) count = 16 * 1024 / slab->final_size; if (count < 50) count = 50; size = count * slab->final_size; /* allocate & init */ frag = cx_alloc0(slab->cx, size + sizeof(struct SlabFrag)); if (!frag) return; list_init(&frag->head); area = (char *)frag + sizeof(struct SlabFrag); /* init objects */ for (i = 0; i < count; i++) { void *obj = area + i * slab->final_size; struct List *head = (struct List *)obj; list_init(head); statlist_append(&slab->freelist, head); } /* register to slab */ slab->total_count += count; statlist_append(&slab->fraglist, &frag->head); } /* get free object from slab */ void *slab_alloc(struct Slab *slab) { struct List *item = statlist_pop(&slab->freelist); if (!item) { grow(slab); item = statlist_pop(&slab->freelist); } if (item) { if (slab->init_func) slab->init_func(item); else memset(item, 0, slab->final_size); } return item; } /* put object back to slab */ void slab_free(struct Slab *slab, void *obj) { struct List *item = obj; list_init(item); statlist_prepend(&slab->freelist, item); } /* total number of objects allocated from slab */ int slab_total_count(const struct Slab *slab) { return slab->total_count; } /* free objects in slab */ int slab_free_count(const struct Slab *slab) { return statlist_count(&slab->freelist); } /* number of objects in use */ int slab_active_count(const struct Slab *slab) { return slab_total_count(slab) - slab_free_count(slab); } static void run_slab_stats(struct Slab *slab, slab_stat_fn cb_func, void *cb_arg) { unsigned free = statlist_count(&slab->freelist); cb_func(cb_arg, slab->name, slab->final_size, free, slab->total_count); } /* call a function for all active slabs */ void slab_stats(slab_stat_fn cb_func, void *cb_arg) { struct Slab *slab; struct List *item; statlist_for_each(item, &slab_list) { slab = container_of(item, struct Slab, head); run_slab_stats(slab, cb_func, cb_arg); } } #else struct Slab { int size; struct StatList obj_list; slab_init_fn init_func; CxMem *cx; }; struct Slab *slab_create(const char *name, unsigned obj_size, unsigned align, slab_init_fn init_func, CxMem *cx) { struct Slab *s = cx_alloc(cx, sizeof(*s)); if (s) { s->size = obj_size; s->init_func = init_func; s->cx = cx; statlist_init(&s->obj_list, "obj_list"); } return s; } void slab_destroy(struct Slab *slab) { struct List *el, *tmp; statlist_for_each_safe(el, &slab->obj_list, tmp) { statlist_remove(&slab->obj_list, el); cx_free(slab->cx, el); } cx_free(slab->cx, slab); } void *slab_alloc(struct Slab *slab) { struct List *o; void *res; o = cx_alloc(slab->cx, sizeof(struct List) + slab->size); if (!o) return NULL; list_init(o); statlist_append(&slab->obj_list, o); res = (void *)(o + 1); if (slab->init_func) slab->init_func(res); return res; } void slab_free(struct Slab *slab, void *obj) { if (obj) { struct List *el = obj; statlist_remove(&slab->obj_list, el - 1); cx_free(slab->cx, el - 1); } } int slab_total_count(const struct Slab *slab) { return 0; } int slab_free_count(const struct Slab *slab) { return 0; } int slab_active_count(const struct Slab *slab) { return 0; } void slab_stats(slab_stat_fn cb_func, void *cb_arg) {} #endif pgbouncer-1.7/lib/usual/slab.h0000664000175000017500000000447312511202014013255 00000000000000/* * Primitive slab allocator. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Slab allocator for same-size objects. * * Basic behaviour: * - On each alloc initializer is called. * - if init func is not given, memset() is done * - init func gets either zeroed obj or old obj from _free(). * 'struct List' on obj start is non-zero. * * ATM custom 'align' larger than malloc() alignment does not work. */ #ifndef _USUAL_SLAB_H_ #define _USUAL_SLAB_H_ #include /** Reference to main */ struct Slab; /** Signature for object init function */ typedef void (*slab_init_fn)(void *obj); /** Create new slab context for specific size */ struct Slab *slab_create(const char *name, unsigned obj_size, unsigned align, slab_init_fn init_func, CxMem *cx); /** Free whole context */ void slab_destroy(struct Slab *slab); /** Allocate single object from slab cache */ void *slab_alloc(struct Slab *slab) _MALLOC _MUSTCHECK; /** Release single object back */ void slab_free(struct Slab *slab, void *obj); /** Return sum of free and used objects */ int slab_total_count(const struct Slab *slab); /** Return number of free objects in cache */ int slab_free_count(const struct Slab *slab); /** Return number of used objects */ int slab_active_count(const struct Slab *slab); /** Signature for stat info callback */ typedef void (*slab_stat_fn)(void *arg, const char *slab_name, unsigned size, unsigned free, unsigned total); /** Run stat info callback on all slabs */ void slab_stats(slab_stat_fn cb_func, void *cb_arg); #endif pgbouncer-1.7/lib/usual/talloc.c0000664000175000017500000010157112560223470013617 00000000000000/* * talloc.c - implementation of "talloc" API from Samba. * * Copyright (c) 2013 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #define MAGIC_USED 0xF100F7 /* allocated block */ #define MAGIC_FREE 0x8600CB /* freed block */ #define MAGIC_MASK 0xFFFFFF /* keep only magic */ #define FLAG_PENDING (1 << 24) /* partially freed */ #define FLAG_USE_MEMLIMIT (1 << 25) /* some parent has memlimit */ #define FLAG_HAS_MEMLIMIT (1 << 26) /* current node has TLimit child */ /* flags parent passes to children */ #define INHERIT_FLAGS (FLAG_USE_MEMLIMIT) /* recursion limit */ #define TALLOC_MAX_DEPTH 10000 /* Don't deal with extreme areas */ #define TALLOC_MAXLEN 0x10000000 /* 256MB */ /* header size that is prepended to each pointer */ #define THSIZE (sizeof(struct THeader)) /* * Prefix on each allocated chunk. * * child_list - internal chunks are put into start of list, * use allocations at the end. freeing happens * from start. this makes sure refs are freed * before other objects. */ struct THeader { uint32_t th_flags; /* flags & magic */ uint32_t size; /* requested size */ CxMem *cx; /* low-level allocation context */ struct THeader *parent; /* parent node, may be NULL */ struct List node; /* node in parent->child_list */ struct List child_list; /* contains child->node */ struct List ref_list; /* contains TRef->ref_node */ const char *name; /* pointer to name string */ talloc_destructor_f destructor; /* function to be called on free */ }; /* * Per-reference struct, attached as child to non-primary parent. */ struct TRef { struct List ref_node; /* node in ->ref_list */ struct TRef *paired_ref; /* track paired helper ref */ }; /* * Track memory limits. Attached as child to * the node limits were set on. FLAG_USE_MEMLIMIT says * if it needs to be checked. */ struct TLimit { ssize_t max_size; ssize_t cur_size; }; /* * Internal helper functions. */ static void log_to_stderr(const char *message); static void do_abort(const char *fmt, ...) _NORETURN; static void do_log(const char *fmt, ...); static void do_dbg(const char *fmt, ...); static int ref_destructor(void *ptr); static bool apply_memlimit(struct THeader *parent, ssize_t delta, bool force); static void move_memlimit(struct THeader *t, struct THeader *newparent, struct THeader *oldparent); static void *find_ptr_from_ref(const struct TRef *ref); /* * Global variables. */ /* log callbacks */ static void (*_log_cb)(const char *message) = log_to_stderr; static void (*_abort_cb)(const char *reason); /* context for parent==NULL */ static void *null_context; /* autofree context */ static void *autofree_ctx; /* names for internal allocations */ static const char MEMLIMIT_NAME[] = ".memlimit"; static const char REF_NAME[] = ".ref"; static const char NULL_NAME[] = ".null-context"; static const char AUTOFREE_NAME[] = ".autofree"; static const char UNNAMED_NAME[] = "UNNAMED"; /* flags to atexit callback */ static int leak_report; static int debug_level; void talloc_set_debug(int level) { debug_level = level; } /* * Internal utils. */ static inline bool has_flags(const struct THeader *t, uint32_t flags) { return (t->th_flags & flags) > 0; } static inline void set_flags(struct THeader *t, uint32_t flags) { t->th_flags |= flags; } static inline void clear_flags(struct THeader *t, uint32_t flags) { t->th_flags &= ~flags; } static inline bool hdr_is_ref(const struct THeader *t) { if (t->destructor == ref_destructor) return true; return false; } static inline void check_magic(const struct THeader *t, const char *pos) { uint32_t magic = t->th_flags & MAGIC_MASK; if (magic != MAGIC_USED) { if (magic == MAGIC_FREE) do_abort("Use after free - %s", pos); else do_abort("Invalid magic - %s", pos); } } static inline void *hdr2ptr(const struct THeader *t) { if (!t) return NULL; check_magic(t, "hdr2ptr"); return (void *)(t + 1); } static inline struct THeader *ptr2hdr(const void *ptr) { struct THeader *t; if (!ptr) return NULL; t = ((struct THeader *)ptr) - 1; check_magic(t, "ptr2hdr"); return t; } static inline size_t total_size(size_t alloc) { return ALIGN(alloc) + THSIZE; } /* if FLAG_CXOWNER is set, this->cx is for children */ static CxMem *get_owner_cx(struct THeader *t) { return t->cx; } /* add refs to start, others to end */ static void add_child(struct THeader *parent, struct THeader *child) { if (parent) { if (hdr_is_ref(child)) list_prepend(&parent->child_list, &child->node); else list_append(&parent->child_list, &child->node); } } /* * actual alloc */ static struct THeader *hdr_alloc_cx(CxMem *cx, struct THeader *parent, size_t len, bool prepend) { struct THeader *t; if (len > TALLOC_MAXLEN) return NULL; if (!parent) parent = ptr2hdr(null_context); if (!apply_memlimit(parent, total_size(len), false)) return NULL; t = cx_alloc(cx, total_size(len)); if (!t) { apply_memlimit(parent, -total_size(len), false); return NULL; } t->th_flags = MAGIC_USED; t->size = len; t->cx = cx; t->parent = parent; list_init(&t->node); list_init(&t->child_list); list_init(&t->ref_list); t->name = NULL; t->destructor = NULL; if (parent) { set_flags(t, parent->th_flags & INHERIT_FLAGS); if (prepend) list_prepend(&parent->child_list, &t->node); else list_append(&parent->child_list, &t->node); } return t; } /* * Allocate */ void *talloc_from_cx(CxMem *cx, size_t len, const char *name) { struct THeader *t; t = hdr_alloc_cx(cx, NULL, len, false); if (!t) return NULL; t->name = name; return hdr2ptr(t); } void *_talloc_const_name(const void *parent, size_t elem_size, size_t count, bool zerofill, const char *name) { struct THeader *t; void *res; size_t size; struct THeader *tparent; CxMem *cx; if (!safe_mul_size(&size, elem_size, count)) return NULL; tparent = ptr2hdr(parent); cx = tparent ? tparent->cx : NULL; t = hdr_alloc_cx(cx, tparent, size, false); if (!t) return NULL; res = hdr2ptr(t); if (zerofill) memset(res, 0, size); t->name = name; return res; } void *_talloc_format_name(const void *parent, size_t elem_size, size_t count, bool zerofill, const char *fmt, ...) { void *res; va_list ap; void *name; res = _talloc_const_name(parent, elem_size, count, zerofill, NULL); if (res) { va_start(ap, fmt); name = talloc_vasprintf(res, fmt, ap); va_end(ap); if (!name) { talloc_free(res); return NULL; } talloc_set_name_const(res, name); } return res; } /* alloc node and put into start of child_list */ static void *internal_alloc_prepend(const void *parent, size_t size, const char *name) { struct THeader *t; void *res; struct THeader *tparent; CxMem *cx; tparent = ptr2hdr(parent); cx = tparent ? tparent->cx : NULL; t = hdr_alloc_cx(cx, tparent, size, true); if (!t) return NULL; res = hdr2ptr(t); t->name = name; return res; } /* * Freeing */ #undef talloc_set_destructor void talloc_set_destructor(const void *ptr, talloc_destructor_f destructor) { struct THeader *t; t = ptr2hdr(ptr); if (t) t->destructor = destructor; } /* attach undying child to live parent */ static void throw_child(struct THeader *t) { struct THeader *parent = t->parent; while (parent && has_flags(parent, FLAG_PENDING)) parent = parent->parent; talloc_reparent(hdr2ptr(t->parent), hdr2ptr(parent), hdr2ptr(t)); } static void free_children(const void *ptr, bool free_name, const char *source_pos) { struct List *el, *tmp; struct THeader *tchild; struct THeader *t; void *child; t = ptr2hdr(ptr); if (!t) return; list_for_each_safe(el, &t->child_list, tmp) { tchild = container_of(el, struct THeader, node); child = hdr2ptr(tchild); if (free_name) { if (child == t->name) t->name = NULL; } else if (child == t->name) { continue; } else if (tchild->name == MEMLIMIT_NAME) { continue; } if (talloc_unlink(ptr, child) != 0) { //do_dbg("DBG: free_children: unlink failed: %s", talloc_get_name(child)); throw_child(tchild); } } } void _talloc_free_children(const void *ptr, const char *source_pos) { free_children(ptr, false, source_pos); } /* what happens when refs are present */ static int free_with_refs(struct THeader *t, const char *source_pos) { struct List *el; struct TRef *ref = NULL; struct THeader *tref; if (t->parent == NULL || hdr2ptr(t->parent) == null_context) { return _talloc_unlink(NULL, hdr2ptr(t->parent), source_pos); } /* check if refs have same parent */ list_for_each(el, &t->ref_list) { ref = container_of(el, struct TRef, ref_node); tref = ptr2hdr(ref); if (tref->parent != t->parent) { do_dbg("free_with_refs: parent fail"); return -1; } } /* always same parent, drop one ref */ return _talloc_free(ref, source_pos); } int _talloc_free(const void *ptr, const char *source_pos) { CxMem *cx; struct THeader *t; struct THeader *tparent; size_t orig_size; do_dbg("DBG: talloc_free(%p) (%s)", ptr, talloc_get_name(ptr)); if (!ptr) return -1; t = ptr2hdr(ptr); /* handle multi-parent free */ if (!list_empty(&t->ref_list)) return free_with_refs(t, source_pos); /* set pending flag */ if (has_flags(t, FLAG_PENDING)) return 0; set_flags(t, FLAG_PENDING); /* run destructor */ if (t->destructor && t->destructor((void *)ptr) < 0) { do_dbg("DBG: talloc_free(%s) - destructor failed", talloc_get_name(ptr)); clear_flags(t, FLAG_PENDING); return -1; } list_del(&t->node); free_children(ptr, true, source_pos); tparent = t->parent; orig_size = t->size; cx = t->cx; /* clear & free */ memset(t, 0, THSIZE); t->size = orig_size; t->th_flags = MAGIC_FREE; t->name = source_pos; cx_free(cx, t); apply_memlimit(tparent, -total_size(orig_size), false); return 0; } /* * Refs */ static struct THeader *find_ref_by_parent(struct THeader *t, struct THeader *tparent) { struct List *el; struct THeader *tref; struct TRef *ref; list_for_each(el, &t->ref_list) { ref = container_of(el, struct TRef, ref_node); tref = ptr2hdr(ref); if (tref->parent == tparent) return tref; } return NULL; } /* remove TRef from ->ref_list */ static int ref_destructor(void *ptr) { struct TRef *ref = ptr; list_del(&ref->ref_node); if (ref->paired_ref) { ref->paired_ref->paired_ref = NULL; ref->paired_ref = NULL; } return 0; } static struct TRef *new_ref(const void *parent, const char *name) { struct TRef *ref; ref = internal_alloc_prepend(parent, sizeof(struct TRef), name ? name : REF_NAME); if (!ref) return NULL; ref->paired_ref = NULL; list_init(&ref->ref_node); talloc_set_destructor(ref, ref_destructor); return ref; } void *_talloc_reference_named(const void *new_parent, const void *ptr, const char *name) { struct TRef *ref; struct THeader *t; t = ptr2hdr(ptr); if (!t) return NULL; ref = new_ref(new_parent, name); if (!ref) return NULL; list_append(&t->ref_list, &ref->ref_node); return (void *)ptr; } int _talloc_unlink(const void *parent, const void *ptr, const char *source_pos) { struct TRef *ref; struct THeader *tref = NULL; struct THeader *tparent; struct THeader *t; int err; t = ptr2hdr(ptr); if (!t) { do_dbg("_talloc_unlink err: no ptr"); return -1; } tparent = ptr2hdr(parent ? parent : null_context); if (t->parent != tparent) { /* ref is not primary */ tref = find_ref_by_parent(t, tparent); if (tref) { err = _talloc_free(hdr2ptr(tref), source_pos); } else { do_dbg("_talloc_unlink err: find_ref_by_parent failed"); err = -1; } } else if (list_empty(&t->ref_list)) { /* ref is primary and there are no other refs */ err = _talloc_free(ptr, source_pos); } else { /* main parent but refs, move to new parent */ /* use first ref to get new parent */ ref = list_pop_type(&t->ref_list, struct TRef, ref_node); tref = ptr2hdr(ref); list_del(&t->node); /* move */ t->parent = tref->parent; add_child(t->parent, t); /* free ref */ err = _talloc_free(ref, source_pos); } return err; } /* * Parent change */ void *talloc_reparent(const void *old_parent, const void *new_parent, const void *ptr) { struct THeader *tnew; struct THeader *told; struct THeader *t; CxMem *cxnew; t = ptr2hdr(ptr); if (!t) return NULL; tnew = ptr2hdr(new_parent ? new_parent : null_context); told = ptr2hdr(old_parent ? old_parent : null_context); if (tnew == t || tnew == told) return (void *)ptr; cxnew = tnew ? tnew->cx : NULL; /* find ref to change parent of */ if (told != t->parent) { t = find_ref_by_parent(t, told); if (!t) { do_log("talloc_reparent failed: did not find old parent\n"); return NULL; } } /* check cx change */ if (t->cx != cxnew) { return NULL; } /* change parent */ list_del(&t->node); add_child(tnew, t); t->parent = tnew; move_memlimit(t, tnew, told); return (void *)ptr; } void *talloc_steal(const void *new_parent, const void *ptr) { struct THeader *t; t = ptr2hdr(ptr); if (!t) return NULL; /* disallow steal when refs are present */ if (!list_empty(&t->ref_list)) return NULL; return talloc_reparent(hdr2ptr(t->parent), new_parent, ptr); } void *_talloc_move(const void *new_parent, void **ptr_p) { void *ptr; ptr = talloc_steal(new_parent, *ptr_p); if (ptr) *ptr_p = NULL; return ptr; } /* * Realloc */ /* node address has moved */ static void fix_list(struct List *node, struct List *oldnode) { if (node->next == oldnode) { list_init(node); } else { node->next->prev = node; node->prev->next = node; } } void *_talloc_realloc(const void *parent, void *ptr, size_t elem_size, size_t count, const char *name) { struct THeader *t1, *t2; struct List *el; CxMem *this_cx; uint32_t old_flags; ssize_t delta; size_t size; /* calc total size */ if (!safe_mul_size(&size, elem_size, count)) return NULL; if (size > TALLOC_MAXLEN) return NULL; /* posix realloc behaviour */ if (!ptr) { if (size == 0) return NULL; return talloc_named_const(parent, size, name); } else if (size == 0) { if (talloc_unlink(parent, ptr) != 0) if (0) do_log("realloc(size=0): unlink failed\n"); return NULL; } t1 = ptr2hdr(ptr); /* disallow realloc when refs are present */ if (!list_empty(&t1->ref_list)) return NULL; /* size difference */ delta = size - t1->size; if (delta == 0) return ptr; /* check limits */ if (!apply_memlimit(t1->parent, delta, false)) return NULL; /* actual realloc of memory */ this_cx = get_owner_cx(t1); old_flags = t1->th_flags; t1->th_flags = MAGIC_FREE; t2 = cx_realloc(this_cx, t1, total_size(size)); if (!t2) { apply_memlimit(t1->parent, -delta, false); t1->th_flags = old_flags; return NULL; } /* fix header after realloc */ t2->th_flags = old_flags; t2->size = size; t2->name = name; /* was memory moved? */ if (t1 == t2) return ptr; /* fix lists after move */ fix_list(&t2->node, &t1->node); fix_list(&t2->child_list, &t1->child_list); fix_list(&t2->ref_list, &t1->ref_list); list_for_each(el, &t2->child_list) { struct THeader *tchild; tchild = container_of(el, struct THeader, node); tchild->parent = t2; } return hdr2ptr(t2); } void *talloc_realloc_fn(const void *parent, void *ptr, size_t size) { return _talloc_realloc(parent, ptr, 1, size, "talloc_realloc_fn"); } /* * memlimit */ /* apply delta to single context */ static bool apply_memlimit_marked(struct THeader *t, ssize_t delta, bool force) { struct List *el; struct THeader *tlim; struct TLimit *lim = NULL; /* find memlimit struct */ list_for_each(el, &t->child_list) { tlim = container_of(el, struct THeader, node); if (tlim->name == MEMLIMIT_NAME) { lim = hdr2ptr(tlim); goto apply; } } return true; apply: /* check limit */ if (delta > 0 && !force) { if (lim->cur_size + delta > lim->max_size) return false; } /* update parent first */ if (!apply_memlimit(t->parent, delta, force)) return false; /* parent is ok, safe to update current struct */ lim->cur_size += delta; if (lim->cur_size < 0) lim->cur_size = 0; return true; } /* apply delta recursively */ static bool apply_memlimit(struct THeader *parent, ssize_t delta, bool force) { struct THeader *t = parent; while (t && has_flags(t, FLAG_USE_MEMLIMIT)) { if (has_flags(t, FLAG_HAS_MEMLIMIT)) return apply_memlimit_marked(t, delta, force); t = t->parent; } return true; } enum { OP_NONE = 0, OP_SET_MEMLIMIT = 1, OP_CLEAR_MEMLIMIT = 2, }; /* count allocated memory and sync flags */ static size_t memlimit_walk(struct THeader *t, int depth, int op) { struct List *el; struct THeader *tchild; size_t size = 0; if (has_flags(t, FLAG_PENDING)) return 0; /* sync memlimit flags */ if (op == OP_SET_MEMLIMIT) { set_flags(t, FLAG_USE_MEMLIMIT); } else if (op == OP_CLEAR_MEMLIMIT) { if (has_flags(t, FLAG_HAS_MEMLIMIT)) op = OP_NONE; else clear_flags(t, FLAG_USE_MEMLIMIT); } /* avoid too deep recursion */ if (depth > TALLOC_MAX_DEPTH) return t->size; /* recurse info child_list */ set_flags(t, FLAG_PENDING); list_for_each(el, &t->child_list) { tchild = container_of(el, struct THeader, node); size += memlimit_walk(tchild, depth + 1, op); } clear_flags(t, FLAG_PENDING); return size + t->size; } static void move_memlimit(struct THeader *t, struct THeader *new_parent, struct THeader *old_parent) { bool oldlim, newlim; ssize_t delta; int op = OP_NONE; /* is memlimit in use? */ newlim = new_parent && has_flags(new_parent, FLAG_USE_MEMLIMIT); oldlim = old_parent && has_flags(old_parent, FLAG_USE_MEMLIMIT); if (!oldlim && !newlim) return; if (oldlim && !newlim) op = OP_CLEAR_MEMLIMIT; else if (newlim && !oldlim) op = OP_SET_MEMLIMIT; /* yes, calc memory size */ delta = memlimit_walk(t, 0, op); /* subtract from old parent */ if (oldlim) apply_memlimit(old_parent, -delta, true); if (newlim) { /* add to new parent */ apply_memlimit(new_parent, delta, true); set_flags(t, FLAG_USE_MEMLIMIT); } else if (!has_flags(t, FLAG_HAS_MEMLIMIT)) { /* drop flag to avoid unnecessary walks */ clear_flags(t, FLAG_USE_MEMLIMIT); } } /* configure memory limits */ int talloc_set_memlimit(const void *ptr, size_t max_size) { struct TLimit *lim = NULL; struct THeader *t, *tmp; struct List *el; if (!ptr) return -1; t = ptr2hdr(ptr); /* find TLimit struct */ if (has_flags(t, FLAG_HAS_MEMLIMIT)) { list_for_each(el, &t->child_list) { tmp = container_of(el, struct THeader, node); if (tmp->name == MEMLIMIT_NAME) { lim = hdr2ptr(tmp); break; } } } /* disable memlimit */ if (max_size == 0) { clear_flags(t, FLAG_HAS_MEMLIMIT); if (lim) talloc_free(lim); return 0; } /* allocate new object */ if (!lim) { lim = internal_alloc_prepend(ptr, sizeof(struct TLimit), MEMLIMIT_NAME); if (!lim) return -1; } /* configure */ lim->max_size = max_size; lim->cur_size = 0; set_flags(t, FLAG_USE_MEMLIMIT | FLAG_HAS_MEMLIMIT); return 0; } /* * Name handling */ const char *talloc_get_name(const void *ptr) { struct THeader *t = ptr2hdr(ptr); return (t && t->name) ? t->name : UNNAMED_NAME; } const char *talloc_set_name(const void *ptr, const char *fmt, ...) { va_list ap; struct THeader *t; t = ptr2hdr(ptr); if (t) { va_start(ap, fmt); t->name = talloc_vasprintf(ptr, fmt, ap); va_end(ap); return t->name; } return NULL; } void talloc_set_name_const(const void *ptr, const char *name) { struct THeader *t; t = ptr2hdr(ptr); if (t) t->name = name; } void *talloc_check_name(const void *ptr, const char *name) { const char *curname; curname = talloc_get_name(ptr); if (curname && name && strcmp(name, curname) == 0) return (void *)ptr; return NULL; } void *_talloc_get_type_abort(const void *ptr, const char *name) { void *res; res = talloc_check_name(ptr, name); if (res) return (void *)ptr; do_abort("wrong type"); } /* * Info */ size_t talloc_get_size(const void *ptr) { struct THeader *t; if (!ptr) return 0; t = ptr2hdr(ptr); return t->size; } bool talloc_is_parent(const void *parent, const void *ptr) { struct THeader *tc; struct THeader *tp; int count = 0; if (!ptr || !parent) return false; tp = ptr2hdr(parent); tc = ptr2hdr(ptr); while (tc) { if (tc->parent == tp) return true; tc = tc->parent; /* dont bother too much */ if (++count >= TALLOC_MAX_DEPTH) break; } return false; } size_t talloc_reference_count(const void *ptr) { struct List *el; size_t cnt = 0; struct THeader *t; t = ptr2hdr(ptr); if (t) { list_for_each(el, &t->ref_list) cnt++; } return cnt; } struct BytesAndCount { size_t bytes; size_t count; }; static void calc_bytes_and_count(const void *ptr, int depth, int max_depth, int is_ref, void *cb_arg) { struct BytesAndCount *state = cb_arg; state->count++; if (!is_ref) { state->bytes += talloc_get_size(ptr); } } size_t talloc_total_size(const void *ptr) { struct BytesAndCount state; memset(&state, 0, sizeof(state)); talloc_report_depth_cb(ptr, 0, TALLOC_MAX_DEPTH, calc_bytes_and_count, &state); return state.bytes; } size_t talloc_total_blocks(const void *ptr) { struct BytesAndCount state; memset(&state, 0, sizeof(state)); talloc_report_depth_cb(ptr, 0, TALLOC_MAX_DEPTH, calc_bytes_and_count, &state); return state.count; } void *talloc_parent(const void *ptr) { struct THeader *t; t = ptr2hdr(ptr); if (t && t->parent) return hdr2ptr(t->parent); return NULL; } const char *talloc_parent_name(const void *ptr) { return talloc_get_name(talloc_parent(ptr)); } void *talloc_find_parent_byname(const void *ptr, const char *name) { struct List *el; struct THeader *tref; struct TRef *ref; struct THeader *t; t = ptr2hdr(ptr); if (!t || !name) return NULL; if (t->parent && !strcmp(name, t->parent->name)) return hdr2ptr(t->parent); list_for_each(el, &t->ref_list) { ref = container_of(el, struct TRef, ref_node); tref = ptr2hdr(ref); if (tref->parent && !strcmp(name, tref->parent->name)) return hdr2ptr(tref->parent); } return NULL; } /* * String copy */ void *talloc_memdup(const void *parent, const void *src, size_t len) { void *res = NULL; if (src) { res = talloc_named_const(parent, len, "talloc_memdup"); if (res) memcpy(res, src, len); } return res; } char *talloc_strdup(const void *parent, const char *s) { return talloc_strndup(parent, s, TALLOC_MAXLEN); } char *talloc_strndup(const void *parent, const char *s, size_t maxlen) { size_t len; char *res; if (!s) return NULL; len = strnlen(s, maxlen); res = talloc_named_const(parent, len + 1, NULL); if (!res) return NULL; memcpy(res, s, len); res[len] = 0; talloc_set_name_const(res, res); return res; } /* * string append */ static size_t buffer_strlen(const void *ptr) { size_t len = talloc_get_size(ptr); return len ? len - 1 : 0; } static char *_concat(char *ptr, bool isbuf, const char *s, size_t maxlen) { size_t plen; size_t slen; /* simple cases */ if (!ptr) return talloc_strndup(ptr, s, maxlen); if (!s) return ptr; /* get lengths */ if (isbuf) { plen = buffer_strlen(ptr); } else { plen = strnlen(ptr, talloc_get_size(ptr)); } slen = strnlen(s, maxlen); /* resize and copy */ ptr = talloc_realloc_fn(ptr, ptr, plen + slen + 1); if (!ptr) return NULL; memcpy(ptr + plen, s, slen + 1); talloc_set_name_const(ptr, ptr); return ptr; } char *talloc_strdup_append(char *ptr, const char *s) { return _concat(ptr, false, s, TALLOC_MAXLEN); } char *talloc_strdup_append_buffer(char *ptr, const char *s) { return _concat(ptr, true, s, TALLOC_MAXLEN); } char *talloc_strndup_append(char *ptr, const char *s, size_t maxlen) { return _concat(ptr, false, s, maxlen); } char *talloc_strndup_append_buffer(char *ptr, const char *s, size_t maxlen) { return _concat(ptr, true, s, maxlen); } /* * printfs */ _PRINTF(4,0) static char *_tprintf(const void *parent, char *ptr, size_t plen, const char *fmt, va_list ap) { char buf[128]; ssize_t len; va_list ap2; char *res; /* print into temp buffer */ va_copy(ap2, ap); len = vsnprintf(buf, sizeof(buf), fmt, ap2); va_end(ap2); if (len < 0) return NULL; /* reserve room */ res = talloc_realloc_fn(parent, ptr, plen + len + 1); if (!res) return NULL; /* fill with string */ if (len < (int)sizeof(buf)) { memcpy(res + plen, buf, len + 1); } else { va_copy(ap2, ap); vsnprintf(res + plen, len + 1, fmt, ap2); va_end(ap2); } talloc_set_name_const(res, res); return res; } char *talloc_vasprintf(const void *parent, const char *fmt, va_list ap) { return _tprintf(parent, NULL, 0, fmt, ap); } char *talloc_vasprintf_append(char *ptr, const char *fmt, va_list ap) { size_t plen = strnlen(ptr, talloc_get_size(ptr)); return _tprintf(NULL, ptr, plen, fmt, ap); } char *talloc_vasprintf_append_buffer(char *ptr, const char *fmt, va_list ap) { size_t plen = buffer_strlen(ptr); return _tprintf(NULL, ptr, plen, fmt, ap); } char *talloc_asprintf(const void *parent, const char *fmt, ...) { char *res; va_list ap; va_start(ap, fmt); res = talloc_vasprintf(parent, fmt, ap); va_end(ap); return res; } char *talloc_asprintf_append(char *ptr, const char *fmt, ...) { char *res; va_list ap; va_start(ap, fmt); res = talloc_vasprintf_append(ptr, fmt, ap); va_end(ap); return res; } char *talloc_asprintf_append_buffer(char *ptr, const char *fmt, ...) { char *res; va_list ap; va_start(ap, fmt); res = talloc_vasprintf_append_buffer(ptr, fmt, ap); va_end(ap); return res; } /* * Autofree */ /* run on program exit */ static void autofree_handler(void) { TALLOC_FREE(autofree_ctx); } static int autofree_destructor(void *ptr) { autofree_ctx = NULL; return 0; } /* create context, register handler with atexit */ void *talloc_autofree_context(void) { static int atexit_ok; /* register atexit handler */ if (!atexit_ok) { if (atexit(autofree_handler) != 0) return NULL; atexit_ok = 1; } /* initialize autofree top-level context */ if (!autofree_ctx) { autofree_ctx = talloc_named_const(NULL, 0, AUTOFREE_NAME); if (!autofree_ctx) return NULL; talloc_set_destructor(autofree_ctx, autofree_destructor); } return autofree_ctx; } /* * Logging */ static void log_to_stderr(const char *message) { fprintf(stderr, "%s\n", message); } _PRINTF(1, 2) static void do_log(const char *fmt, ...) { char buf[128]; va_list ap; if (!_log_cb) return; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); _log_cb(buf); } _PRINTF(1, 2) static void do_dbg(const char *fmt, ...) { char buf[128]; va_list ap; if (!_log_cb || debug_level == 0) return; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); _log_cb(buf); } _PRINTF(1, 0) static void do_abort(const char *fmt, ...) { char buf[128]; va_list ap; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); if (_abort_cb) _abort_cb(buf); else if (_log_cb) _log_cb(buf); abort(); } void talloc_set_log_fn(void (*log_fn)(const char *message)) { _log_cb = log_fn; } void talloc_set_log_stderr(void) { _log_cb = log_to_stderr; } void talloc_set_abort_fn(void (*abort_fn)(const char *reason)) { _abort_cb = abort_fn; } /* * Tracking. */ static void atexit_leak_report(void) { if (leak_report == 2) talloc_report_full(NULL, stderr); else talloc_report(NULL, stderr); } /* activate null context as child of autofree context */ void talloc_enable_null_tracking(void) { void *ctx; if (!null_context) { ctx = talloc_named_const(NULL, 0, NULL_NAME); if (ctx && autofree_ctx) talloc_reparent(NULL, ctx, autofree_ctx); null_context = ctx; } } /* activate null context */ void talloc_enable_null_tracking_no_autofree(void) { if (!null_context) null_context = talloc_named_const(NULL, 0, NULL_NAME); } /* move childs away from null context */ void talloc_disable_null_tracking(void) { struct THeader *t, *tchild; struct List *el, *tmp; if (!null_context) return; t = ptr2hdr(null_context); list_for_each_safe(el, &t->child_list, tmp) { tchild = container_of(el, struct THeader, node); list_del(&tchild->node); tchild->parent = NULL; } TALLOC_FREE(null_context); } void talloc_enable_leak_report(void) { if (!leak_report) atexit(atexit_leak_report); leak_report = 1; } void talloc_enable_leak_report_full(void) { if (!leak_report) atexit(atexit_leak_report); leak_report = 2; } /* * Reporting */ static void *find_ptr_from_ref(const struct TRef *ref) { struct List *el; struct TRef *ref2; struct THeader *tref2; struct THeader *t; list_for_each(el, &ref->ref_node) { /* * Actual struct is not known here - THeader * must have both ->destructor & ->ref_list * accessible locations. */ ref2 = container_of(el, struct TRef, ref_node); /* this check must work it out */ tref2 = ((struct THeader *)ref2) - 1; if (hdr_is_ref(tref2)) continue; /* it is not TRef, so it must be parent's THeader */ t = container_of(el, struct THeader, ref_list); return hdr2ptr(t); } return NULL; } void talloc_report_depth_cb(const void *ptr, int depth, int max_depth, void (*cb_func)(const void *ptr, int depth, int max_depth, int is_ref, void *cb_arg), void *cb_arg) { struct List *el; struct THeader *tchild; struct THeader *t; t = ptr2hdr(ptr); if (!t) t = ptr2hdr(null_context); if (!t) return; if (has_flags(t, FLAG_PENDING)) return; /* run callback */ if (hdr_is_ref(t)) { void *ptr2 = find_ptr_from_ref(ptr); if (ptr2) cb_func(ptr2, depth, max_depth, true, cb_arg); return; } cb_func(ptr, depth, max_depth, false, cb_arg); /* check depth */ depth++; if (depth > max_depth) return; /* loop over childs */ set_flags(t, FLAG_PENDING); list_for_each(el, &t->child_list) { tchild = container_of(el, struct THeader, node); talloc_report_depth_cb(hdr2ptr(tchild), depth, max_depth, cb_func, cb_arg); } clear_flags(t, FLAG_PENDING); } static void report_cb(const void *ptr, int depth, int max_depth, int is_ref, void *cb_arg) { FILE *f = cb_arg; struct BytesAndCount state; const char *name; int indent; char limitbuf[128]; struct THeader *t; indent = depth * 2; t = ptr2hdr(ptr); name = talloc_get_name(ptr); limitbuf[0] = 0; if (name == MEMLIMIT_NAME) { struct TLimit *lim = hdr2ptr(t); snprintf(limitbuf, sizeof(limitbuf), "%s [cur=%" PRIuZ " max=%" PRIuZ "]", name, lim->cur_size, lim->max_size); name = limitbuf; } memset(&state, 0, sizeof(state)); talloc_report_depth_cb(ptr, 0, TALLOC_MAX_DEPTH, calc_bytes_and_count, &state); if (depth == 0) { fprintf(f, "talloc report on '%s' (total %" PRIuZ " bytes in %" PRIuZ " blocks)%s\n", name, state.bytes, state.count, limitbuf); return; } if (is_ref) { fprintf(f, "%*sreference to %s\n", indent, " ", name); return; } fprintf(f, "%*s%-*s contains %6" PRIuZ " bytes in %6" PRIuZ " blocks%s\n", indent, " ", indent < 40 ? 40 - indent : 0, name, state.bytes, state.count, limitbuf); } void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f) { talloc_report_depth_cb(ptr, depth, max_depth, report_cb, f); } void talloc_report(const void *ptr, FILE *f) { talloc_report_depth_file(ptr, 0, 1, f); } void talloc_report_full(const void *ptr, FILE *f) { talloc_report_depth_file(ptr, 0, TALLOC_MAX_DEPTH, f); } void talloc_show_parents(const void *ptr, FILE *file) { struct THeader *t, *tref; struct TRef *ref; struct List *el; if (!ptr) { fprintf(file, "No parents for NULL\n"); return; } fprintf(file, "Parents for '%s'\n", talloc_get_name(ptr)); t = ptr2hdr(ptr); if (t->parent) { fprintf(file, "\t%s\n", talloc_get_name(hdr2ptr(t->parent))); } else { fprintf(file, "\tNULL context\n"); } list_for_each(el, &t->ref_list) { ref = container_of(el, struct TRef, ref_node); tref = ptr2hdr(ref); fprintf(file, "\t%s\n", talloc_get_name(hdr2ptr(tref->parent))); } } /* * Talloc-backed CxMem. * * Makes CxMem modules work with talloc. */ static void *cxt_alloc(void *ctx, size_t size) { return talloc_size(ctx, size); } static void cxt_free(void *ctx, const void *ptr) { if (talloc_unlink(ctx, ptr) != 0) do_log("cxt_free: talloc_unlink failed\n"); } static void *cxt_realloc(void *ctx, void *ptr, size_t len) { return talloc_realloc_size(ctx, ptr, len); } static void cxt_destroy(void *ctx) { if (talloc_free(ctx) != 0) do_log("cxt_destroy: talloc_free failed\n"); } static const struct CxOps cxt_ops = { cxt_alloc, cxt_realloc, cxt_free, cxt_destroy, }; CxMem *talloc_as_cx(const void *parent, const char *name) { struct CxMem *cx; if (!name) name = ".cxmem"; cx = talloc_named_const(parent, sizeof(struct CxMem), name); if (!cx) return NULL; cx->ops = &cxt_ops; cx->ctx = cx; return cx; } pgbouncer-1.7/lib/usual/logging.h0000664000175000017500000001166412511203511013765 00000000000000/* * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Logging framework for unix services. * * * Supported outputs: * - syslog * - log file * - stderr * * @section logging_prefix Logging context * * It is possible to pass context info to all logging calls * and later add details to log lines or to filter based on it. * * Each call references 2 macros: * - LOG_CONTEXT_DEF - which can define/call any variables * - LOG_CONTEXT - which should return a pointer variable. * * Later, global callback function \ref logging_prefix_cb * will get this pointer with destination buffer and can either * add more info for log line or tell to skip logging this message. */ #ifndef _USUAL_LOGGING_H_ #define _USUAL_LOGGING_H_ #include /* internal log levels */ enum LogLevel { LG_FATAL = 0, LG_ERROR = 1, LG_WARNING = 2, LG_STATS = 3, LG_INFO = 4, LG_DEBUG = 5, LG_NOISE = 6, }; #ifndef LOG_CONTEXT_DEF /** Example: Prepare dummy context pointer */ #define LOG_CONTEXT_DEF void *_log_ctx = NULL #endif #ifndef LOG_CONTEXT /** Example: Reference dummy context pointer */ #define LOG_CONTEXT _log_ctx #endif /** * Signature for logging_prefix_cb. Return value is either added string length in dst * or negative value to skip logging. */ typedef int (*logging_prefix_fn_t)(enum LogLevel lev, void *ctx, char *dst, unsigned int dstlen); /** * Optional global callback for each log line. * * It can either add info to log message or skip logging it. */ extern logging_prefix_fn_t logging_prefix_cb; /** * Global verbosity level. * * 0 - show only info level msgs (default) * 1 - show debug msgs (log_debug) * 2 - show noise msgs (log_noise) */ extern int cf_verbose; /** * Toggle logging to stderr. Default: 1. * daemon.c turns this off if goes to background */ extern int cf_quiet; /** * Logfile location, default NULL */ extern const char *cf_logfile; /** Syslog on/off */ extern int cf_syslog; /** ident for syslog, if NULL syslog is disabled (default) */ extern const char *cf_syslog_ident; /** Facility name */ extern const char *cf_syslog_facility; /** Max log level for syslog writer */ extern enum LogLevel cf_syslog_level; /** Max log level for logfile writer */ extern enum LogLevel cf_logfile_level; /** Max log level for stderr writer */ extern enum LogLevel cf_stderr_level; /* * Internal API. */ /* non-fatal logging */ void log_generic(enum LogLevel level, void *ctx, const char *s, ...) _PRINTF(3, 4); /* this is also defined in base.h for Assert() */ void log_fatal(const char *file, int line, const char *func, bool show_perror, void *ctx, const char *s, ...) _PRINTF(6, 7); /* * Public API */ /** Log error message */ #define log_error(...) do { LOG_CONTEXT_DEF; \ log_generic(LG_ERROR, LOG_CONTEXT, __VA_ARGS__); \ } while (0) /** Log warning message */ #define log_warning(...) do { LOG_CONTEXT_DEF; \ log_generic(LG_WARNING, LOG_CONTEXT, __VA_ARGS__); \ } while (0) /** Log stats (liveness) message */ #define log_stats(...) do { LOG_CONTEXT_DEF; \ log_generic(LG_STATS, LOG_CONTEXT, __VA_ARGS__); \ } while (0) /** Log info message */ #define log_info(...) do { LOG_CONTEXT_DEF; \ log_generic(LG_INFO, LOG_CONTEXT, __VA_ARGS__); \ } while (0) /** Log debug message */ #define log_debug(...) do { LOG_CONTEXT_DEF; \ if (unlikely(cf_verbose > 0)) \ log_generic(LG_DEBUG, LOG_CONTEXT, __VA_ARGS__); \ } while (0) /** Log debug noise */ #define log_noise(...) do { LOG_CONTEXT_DEF; \ if (unlikely(cf_verbose > 1)) \ log_generic(LG_NOISE, LOG_CONTEXT, __VA_ARGS__); \ } while (0) /** Log and die. It also logs source location */ #define fatal(...) do { LOG_CONTEXT_DEF; \ log_fatal(__FILE__, __LINE__, __func__, false, LOG_CONTEXT, __VA_ARGS__); \ exit(1); } while (0) /** Log strerror and die. Error message also includes strerror(errno) */ #define fatal_perror(...) do { LOG_CONTEXT_DEF; \ log_fatal(__FILE__, __LINE__, __func__, true, LOG_CONTEXT, __VA_ARGS__); \ exit(1); } while (0) /** Less verbose fatal() */ #define die(...) do { LOG_CONTEXT_DEF; \ log_generic(LG_FATAL, LOG_CONTEXT, __VA_ARGS__); \ exit(1); } while (0) /** * Close open logfiles and syslog. * * Useful when rotating log files. */ void reset_logging(void); #endif pgbouncer-1.7/lib/usual/fileutil.c0000664000175000017500000000615212511202014014140 00000000000000/* * File access utils. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #ifdef HAVE_SYS_MMAN_H #include #endif #include #include #include /* * Load text file into C string. */ void *load_file(const char *fn, size_t *len_p) { struct stat st; char *buf = NULL; int res; FILE *f; res = stat(fn, &st); if (res < 0) return NULL; buf = malloc(st.st_size + 1); if (!buf) return NULL; f = fopen(fn, "r"); if (!f) { free(buf); return NULL; } if ((res = fread(buf, 1, st.st_size, f)) < 0) { free(buf); fclose(f); return NULL; } fclose(f); buf[res] = 0; if (len_p) *len_p = res; return buf; } /* * Read file line-by-line, call user func on each. */ bool foreach_line(const char *fn, procline_cb proc_line, void *arg) { char *ln = NULL; size_t len = 0; ssize_t res; FILE *f = fopen(fn, "rb"); bool ok = false; if (!f) return false; while (1) { res = getline(&ln, &len, f); if (res < 0) { if (feof(f)) ok = true; break; } if (!proc_line(arg, ln, res)) break; } fclose(f); free(ln); return ok; } /* * Find file size. */ ssize_t file_size(const char *fn) { struct stat st; if (stat(fn, &st) < 0) return -1; return st.st_size; } /* * Map a file into mem. */ #ifdef HAVE_MMAP int map_file(struct MappedFile *m, const char *fname, int rw) { struct stat st; m->fd = open(fname, rw ? O_RDWR : O_RDONLY); if (m->fd < 0) return -1; if (fstat(m->fd, &st) < 0) { close(m->fd); return -1; } m->len = st.st_size; m->ptr = mmap(NULL, m->len, PROT_READ | (rw ? PROT_WRITE : 0), MAP_SHARED, m->fd, 0); if (m->ptr == MAP_FAILED) { close(m->fd); return -1; } return 0; } void unmap_file(struct MappedFile *m) { munmap(m->ptr, m->len); close(m->fd); m->ptr = NULL; m->fd = 0; } #endif #ifndef HAVE_GETLINE /* * Read line from FILE with dynamic allocation. */ int getline(char **line_p, size_t *size_p, void *_f) { FILE *f = _f; char *p; int len = 0; if (!*line_p || *size_p < 128) { p = realloc(*line_p, 512); if (!p) return -1; *line_p = p; *size_p = 512; } while (1) { p = fgets(*line_p + len, *size_p - len, f); if (!p) return len ? len : -1; len += strlen(p); if ((*line_p)[len - 1] == '\n') return len; p = realloc(*line_p, *size_p * 2); if (!p) return -1; *line_p = p; *size_p *= 2; } } #endif pgbouncer-1.7/lib/usual/regex.h0000664000175000017500000001533012511202014013440 00000000000000/* * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * POSIX regular expession API, provided by either libc or internally. * * The internal regex engine is only activated if OS does not provide * @ref uregex_links "" (eg. Windows) or if * --with-internal-regex is used when configuring @ref libusual. * * @section uregex Features of internal regex (uregex). * * Simple recursive matcher, only features are small size * and POSIX compatibility. Supports both Extended Regular Expressions (ERE) * and Basic Regular Expressions (BRE). * * @section uregex_syntax Supported syntax * @code * Both: . * ^ $ [] [[:cname:]] * ERE: () {} | + ? * BRE: \(\) \{\} \1-9 * @endcode * * With REG_RELAXED_SYNTAX, following common escapes will be available: * @code * Both: \b\B\d\D\s\S\w\W * BRE: \| * ERE: \1-9 * @endcode * * With REG_RELAXED_MATCHING it returns the first match found after applying * leftmost-longest to all elements. It skips the combinatorics to turn it * into guaranteed-longest match. * * @section uregex_skip Skipped POSIX features * - collation classes: [[. .]] * - equivalence classes: [[= =]] * - char ranges by locale order: [a-z] (byte order will be used) * - multi-byte chars: UTF-8 * * @section uregex_globaldefs Global defines * - USUAL_RELAXED_REGEX * - USE_INTERNAL_REGEX * * @section uregex_links Compatibility * * - * POSIX-2008 spec - by default uRegex run in mode where only * features specified by POSIX are available. * * - * AT\&T Research regex(3) regression tests - uRegex follows the interpretation * given there and fully passes the testsuite. */ #ifndef _USUAL_REGEX_H_ #define _USUAL_REGEX_H_ #include #if !defined(USE_INTERNAL_REGEX) && defined(HAVE_REGEX_H) && defined(HAVE_REGCOMP) #define USE_SYSTEM_REGEX #endif #ifdef USE_SYSTEM_REGEX #include #else /* * uRegex defines */ /** * @name Standard flags to regcomp() * @{ */ /** Use POSIX Extended Regex Syntax instead of Basic Syntax */ #define REG_EXTENDED (1 << 0) /** Do case-insensitive matching */ #define REG_ICASE (1 << 1) /** Do case-insensitive matching */ #define REG_NOSUB (1 << 2) /** Do case-insensitive matching */ #define REG_NEWLINE (1 << 3) /* @} */ /** * @name Standard flags to regexec() * @{ */ /** The start of string is not beginning of line, so ^ should not match */ #define REG_NOTBOL (1 << 4) /** The end of string is not end of line, so $ should not match */ #define REG_NOTEOL (1 << 5) /* @} */ /** * @name Standard error codes * @{ */ /** Match not found */ #define REG_NOMATCH 1 /** Bad {} repeat specification */ #define REG_BADBR 2 /** General problem with regular expression */ #define REG_BADPAT 3 /** Repeat used without preceding non-repeat element */ #define REG_BADRPT 4 /** Syntax error with {} */ #define REG_EBRACE 5 /** Syntax error with [] */ #define REG_EBRACK 6 /** Bad collation reference */ #define REG_ECOLLATE 7 /** Bad character class reference */ #define REG_ECTYPE 8 /** Trailing backslack */ #define REG_EESCAPE 9 /** Syntax error with () */ #define REG_EPAREN 10 /** Bad endpoint in range */ #define REG_ERANGE 11 /** No memory */ #define REG_ESPACE 12 /** Bad subgroup reference */ #define REG_ESUBREG 13 /* @} */ /** * @name Other defines * @{ */ #undef RE_DUP_MAX /** Max count user can enter via {} */ #define RE_DUP_MAX 0x7ffe /* @} */ /** * @name Non-standard flags for regcomp() * @{ */ /** * Allow few common non-standard escapes: * @code * \b - word-change * \B - not word change * \d - digit * \D - non-digit * \s - space * \S - non-space * \w - word char * \W - non-word char * \/ - / * @endcode */ #define REG_RELAXED_SYNTAX (1 << 14) /** * Dont permute groups in attempt to get longest match. * * May give minor speed win at the expense of strict * POSIX compatibility. */ #define REG_RELAXED_MATCHING (1 << 15) /** Turn on both REG_RELAXED_SYNTAX and REG_RELAXED_MATCHING */ #define REG_RELAXED (REG_RELAXED_SYNTAX | REG_RELAXED_MATCHING) /* @} */ /* turn them permanently on */ #ifdef USUAL_RELAXED_REGEX #undef REG_EXTENDED #define REG_EXTENDED (1 | REG_RELAXED) #endif /** * Compiled regex. * * It has only one standard field - re_nsub, * rest are implementation-specific. */ typedef struct { /** Number of subgroups in expression */ int re_nsub; void *internal; } regex_t; /** Type for offset in match */ typedef long regoff_t; /** Match location */ typedef struct { regoff_t rm_so; /**< Start offset */ regoff_t rm_eo; /**< End offset */ } regmatch_t; /* avoid name conflicts */ #define regcomp(a,b,c) usual_regcomp(a,b,c) #define regexec(a,b,c,d,e) usual_regexec(a,b,c,d,e) #define regerror(a,b,c,d) usual_regerror(a,b,c,d) #define regfree(a) usual_regfree(a) /** * Compile regex. * * @param rx Pre-allocated @ref regex_t structure to fill. * @param re Regex as zero-terminated string. * @param flags See above for regcomp() flags. */ int regcomp(regex_t *rx, const char *re, int flags); /** * Execute regex on a string. * * @param rx Regex previously initialized with regcomp() * @param str Zero-terminated string to match * @param nmatch Number of matches in pmatch * @param pmatch Array of matches. * @param eflags Execution flags. Supported flags: @ref REG_NOTBOL, @ref REG_NOTEOL */ int regexec(const regex_t *rx, const char *str, size_t nmatch, regmatch_t pmatch[], int eflags); /** * Give error description. * * @param err Error code returned by regcomp() or regexec() * @param rx Regex structure used in regcomp() or regexec() * @param dst Destination buffer * @param dstlen Size of dst */ size_t regerror(int err, const regex_t *rx, char *dst, size_t dstlen); /** * Free resources allocated by regcomp(). * @param rx Regex previously filled by regcomp() */ void regfree(regex_t *rx); #endif /* !USE_SYSTEM_REGEX */ #endif /* _USUAL_REGEX_H_ */ pgbouncer-1.7/lib/usual/mdict.h0000664000175000017500000000555712511202014013440 00000000000000/* * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * * Minimal dict. */ #ifndef _USUAL_MDICT_H_ #define _USUAL_MDICT_H_ #include #include /** Dict reference */ struct MDict; /** Create new emtpy dict */ struct MDict *mdict_new(CxMem *cx); /** Free dict */ void mdict_free(struct MDict *dict); /** Get value as MBuf from string */ const struct MBuf *mdict_get_buf(struct MDict *dict, const char *key, unsigned klen); /** Get value from dict */ const char *mdict_get_str(struct MDict *dict, const char *key, unsigned klen); /** Put string to dict */ bool mdict_put_str(struct MDict *dict, const char *key, unsigned klen, const char *val, unsigned vlen); /** Remove a key from dict */ bool mdict_del_key(struct MDict *dict, const char *key, unsigned klen); /** Signature for walker callback */ typedef bool (*mdict_walker_f)(void *arg, const struct MBuf *k, const struct MBuf *v); /** Walk over dict */ bool mdict_walk(struct MDict *dict, mdict_walker_f cb_func, void *cb_arg); /* * Simple API that calculates strlen inline. */ /** Get value from dict */ static inline const char *mdict_get(struct MDict *dict, const char *key) { return mdict_get_str(dict, key, strlen(key)); } /** Put zero-terminated key and value to dict */ static inline bool mdict_put(struct MDict *dict, const char *key, const char *val) { unsigned klen = strlen(key); unsigned vlen = val ? strlen(val) : 0; return mdict_put_str(dict, key, klen, val, vlen); } /** Put MBuf to dict */ static inline bool mdict_put_buf(struct MDict *dict, const char *key, const struct MBuf *buf) { unsigned klen = strlen(key); const char *val = buf ? mbuf_data(buf) : NULL; unsigned vlen = buf ? mbuf_written(buf) : 0; return mdict_put_str(dict, key, klen, val, vlen); } /** Remove value from dict */ static inline bool mdict_del(struct MDict *dict, const char *key) { return mdict_del_key(dict, key, strlen(key)); } /** Urldecode string and add keys with values to dict */ bool mdict_urldecode(struct MDict *dict, const char *str, unsigned len); /** Urlencode dict to string */ bool mdict_urlencode(struct MDict *dict, struct MBuf *dst); #endif pgbouncer-1.7/lib/usual/safeio.c0000664000175000017500000001162012511202014013565 00000000000000/* * libusual - Utility library for C * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Wrappers around regular I/O functions (send/recv/read/write) * that survive EINTR and also can log problems. */ #include #include #include #include #include int safe_read(int fd, void *buf, int len) { int res; loop: res = read(fd, buf, len); if (res < 0 && errno == EINTR) goto loop; return res; } int safe_write(int fd, const void *buf, int len) { int res; loop: res = write(fd, buf, len); if (res < 0 && errno == EINTR) goto loop; return res; } int safe_recv(int fd, void *buf, int len, int flags) { int res; char ebuf[128]; loop: res = recv(fd, buf, len, flags); if (res < 0 && errno == EINTR) goto loop; if (res < 0) log_noise("safe_recv(%d, %d) = %s", fd, len, strerror_r(errno, ebuf, sizeof(ebuf))); else if (cf_verbose > 2) log_noise("safe_recv(%d, %d) = %d", fd, len, res); return res; } int safe_send(int fd, const void *buf, int len, int flags) { int res; char ebuf[128]; loop: res = send(fd, buf, len, flags); if (res < 0 && errno == EINTR) goto loop; if (res < 0) log_noise("safe_send(%d, %d) = %s", fd, len, strerror_r(errno, ebuf, sizeof(ebuf))); else if (cf_verbose > 2) log_noise("safe_send(%d, %d) = %d", fd, len, res); return res; } int safe_close(int fd) { int res; #ifndef WIN32 /* * POSIX says close() can return EINTR but fd state is "undefined" * later. Seems Linux and BSDs close the fd anyway and EINTR is * simply informative. Thus retry is dangerous. */ res = close(fd); #else /* * Seems on windows it can returns proper EINTR but only when * WSACancelBlockingCall() is called. As we don't do it, * ignore EINTR on win32 too. */ res = closesocket(fd); #endif if (res < 0) { char ebuf[128]; log_warning("safe_close(%d) = %s", fd, strerror_r(errno, ebuf, sizeof(ebuf))); } else if (cf_verbose > 2) { log_noise("safe_close(%d) = %d", fd, res); } /* ignore EINTR */ if (res < 0 && errno == EINTR) return 0; return res; } int safe_recvmsg(int fd, struct msghdr *msg, int flags) { int res; char ebuf[128]; loop: res = recvmsg(fd, msg, flags); if (res < 0 && errno == EINTR) goto loop; if (res < 0) log_warning("safe_recvmsg(%d, msg, %d) = %s", fd, flags, strerror_r(errno, ebuf, sizeof(ebuf))); else if (cf_verbose > 2) log_noise("safe_recvmsg(%d, msg, %d) = %d", fd, flags, res); return res; } int safe_sendmsg(int fd, const struct msghdr *msg, int flags) { int res; int msgerr_count = 0; char ebuf[128]; loop: res = sendmsg(fd, msg, flags); if (res < 0 && errno == EINTR) goto loop; if (res < 0) { log_warning("safe_sendmsg(%d, msg[%d,%d], %d) = %s", fd, (int)msg->msg_iov[0].iov_len, (int)msg->msg_controllen, flags, strerror_r(errno, ebuf, sizeof(ebuf))); /* with ancillary data on blocking socket OSX returns * EMSGSIZE instead of blocking. try to solve it by waiting */ if (errno == EMSGSIZE && msgerr_count < 20) { struct timeval tv = {1, 0}; log_warning("trying to sleep a bit"); select(0, NULL, NULL, NULL, &tv); msgerr_count++; goto loop; } } else if (cf_verbose > 2) log_noise("safe_sendmsg(%d, msg, %d) = %d", fd, flags, res); return res; } int safe_connect(int fd, const struct sockaddr *sa, socklen_t sa_len) { int res; char buf[128]; char ebuf[128]; loop: res = connect(fd, sa, sa_len); if (res < 0 && errno == EINTR) goto loop; if (res < 0 && (errno != EINPROGRESS || cf_verbose > 2)) log_noise("connect(%d, %s) = %s", fd, sa2str(sa, buf, sizeof(buf)), strerror_r(errno, ebuf, sizeof(ebuf))); else if (cf_verbose > 2) log_noise("connect(%d, %s) = %d", fd, sa2str(sa, buf, sizeof(buf)), res); return res; } int safe_accept(int fd, struct sockaddr *sa, socklen_t *sa_len_p) { int res; char buf[128]; char ebuf[128]; loop: res = accept(fd, sa, sa_len_p); if (res < 0 && errno == EINTR) goto loop; if (res < 0) log_noise("safe_accept(%d) = %s", fd, strerror_r(errno, ebuf, sizeof(ebuf))); else if (cf_verbose > 2) log_noise("safe_accept(%d) = %d (%s)", fd, res, sa2str(sa, buf, sizeof(buf))); return res; } pgbouncer-1.7/lib/usual/utf8.c0000664000175000017500000001162712511203511013217 00000000000000/* * Low-level UTF8 handling. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #define u8head(c, mask) (((c) & (mask | (mask >> 1))) == mask) #define u8tail(c) u8head(c, 0x80) /* * conservative utf8 decoder * * if invalid char, advance src pointer by one and return * negative byte value. this can be ignored or replaced. */ int utf8_get_char(const char **src_p, const char *_srcend) { uint32_t c; const uint8_t *srcend = (uint8_t *)_srcend; const uint8_t *p = (uint8_t *)(*src_p); /* * 0xxx xxxx -> len=1 * 10xx xxxx -> tail byte * 110x xxxx -> len=2 * 1110 xxxx -> len=3 * 1111 0xxx -> len=4 */ if (p[0] < 0x80) { c = *p++; } else if (u8head(p[0], 0xC0)) { if (p + 2 > srcend) goto eos; if (!u8tail(p[1])) goto bad_enc; c = ((p[0] & 0x1F) << 6) | (p[1] & 0x3F); if (c < 0x80) goto bad_enc; p += 2; } else if (u8head(p[0], 0xE0)) { if (p + 3 > srcend) goto eos; if (!u8tail(p[1]) || !u8tail(p[2])) goto bad_enc; c = ((p[0] & 0x0F) << 12) | ((p[1] & 0x3F) << 6) | (p[2] & 0x3F); if ((c < 0x800) || ((c & 0xF800) == 0xD800)) goto bad_enc; p += 3; } else if (u8head(p[0], 0xF0)) { if (p + 4 > srcend) goto eos; if (!u8tail(p[1]) || !u8tail(p[2]) || !u8tail(p[3])) goto bad_enc; c = ((p[0] & 0x07) << 18) | ((p[1] & 0x3F) << 12) | ((p[2] & 0x3F) << 6) | (p[3] & 0x3F); if (c < 0x10000 || c > 0x10FFFF) goto bad_enc; p += 4; } else { goto bad_enc; } *src_p = (char *)p; return c; bad_enc: eos: c = p[0]; *src_p = (char *)p + 1; return -(int)c; } /* encode one char - skip invalid ones */ bool utf8_put_char(unsigned int c, char **dst_p, const char *dstend) { char *dst = *dst_p; if (c < 0x80) { if (dst + 1 > dstend) goto no_room; *dst++ = c; } else if (c < 0x800) { if (dst + 2 > dstend) goto no_room; *dst++ = 0xC0 | (c >> 6); *dst++ = 0x80 | (c & 0x3F); } else if (c < 0x10000) { if (dst + 3 > dstend) goto no_room; if (c < 0xD800 || c > 0xDFFF) { *dst++ = 0xE0 | (c >> 12); *dst++ = 0x80 | ((c >> 6) & 0x3F); *dst++ = 0x80 | (c & 0x3F); } } else if (c <= 0x10FFFF) { if (dst + 4 > dstend) goto no_room; *dst++ = 0xF0 | (c >> 18); *dst++ = 0x80 | ((c >> 12) & 0x3F); *dst++ = 0x80 | ((c >> 6) & 0x3F); *dst++ = 0x80 | (c & 0x3F); } *dst_p = dst; return true; no_room: return false; } int utf8_char_size(unsigned int c) { if (c < 0x80) return 1; if (c < 0x800) return 2; if (c < 0x10000) return 3; return 4; } int utf8_seq_size(unsigned char b) { if (b < 0x80) return 1; if (b < 0xC2) return 0; if (b < 0xE0) return 2; if (b < 0xF0) return 3; if (b < 0xF5) return 4; return 0; } /* * 7f: c1bf (+1) * 80: c280 * 7ff: dfbf * 7ff: e09fbf (+1) * 800: e0a080 * ffff: efbfbf * ffff: f08fbfbf (+1) * 10000: f0908080 * 10ffff: f48fbfbf */ int utf8_validate_seq(const char *src, const char *srcend) { const unsigned char *u = (unsigned char *)src; const unsigned char *uend = (unsigned char *)srcend; if (u[0] < 0x80) { /* ascii */ if (u[0] == 0) goto invalid; return 1; } else if (u[0] < 0xC2) { /* tail byte as first byte */ goto invalid; } else if (u[0] < 0xE0) { /* 1 tail byte */ if (u + 2 > uend) goto invalid; if ((u[1] & 0xC0) != 0x80) goto invalid; return 2; } else if (u[0] < 0xF0) { /* 2 tail bytes */ if (u + 3 > uend) goto invalid; if (u[0] == 0xE0 && u[1] < 0xA0) goto invalid; if (u[0] == 0xED && u[1] >= 0xA0) goto invalid; if ((u[1] & 0xC0) != 0x80) goto invalid; if ((u[2] & 0xC0) != 0x80) goto invalid; return 3; } else if (u[0] < 0xF5) { /* 3-tail bytes */ if (u + 4 > uend) goto invalid; if (u[0] == 0xF0 && u[1] < 0x90) goto invalid; if (u[0] == 0xF4 && u[1] > 0x8F) goto invalid; if ((u[1] & 0xC0) != 0x80) goto invalid; if ((u[2] & 0xC0) != 0x80) goto invalid; if ((u[3] & 0xC0) != 0x80) goto invalid; return 4; } invalid: return 0; } bool utf8_validate_string(const char *src, const char *end) { unsigned int n; while (src < end) { if (*src & 0x80) { n = utf8_validate_seq(src, end); if (n == 0) return false; src += n; } else if (*src == '\0') { return false; } else { src++; } } return true; } pgbouncer-1.7/lib/usual/crypto/0000775000175000017500000000000012635051616013574 500000000000000pgbouncer-1.7/lib/usual/crypto/keccak.h0000664000175000017500000000450512511203511015074 00000000000000/* * Keccak implementation. * * Copyright (c) 2012 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * Simple API to Keccak1600 permutation + sponge. */ #ifndef _USUAL_CRYPTO_KECCAK_H_ #define _USUAL_CRYPTO_KECCAK_H_ #include /** * Keccak state structure for all modes. */ struct KeccakContext { /* 5*5*64 bit state */ union { uint64_t state64[25]; uint32_t state32[2*25]; } u; uint32_t pos; /* current byte position in buffer */ uint32_t rbytes; /* rate (= block size) in bytes */ }; /** * Set up state with specified capacity. * * Returns 1 if successful, 0 if invalid capacity. */ int keccak_init(struct KeccakContext *ctx, unsigned int capacity); /** * Hash additional data. */ void keccak_absorb(struct KeccakContext *ctx, const void *data, size_t len); /** * Extract bytes from state. */ void keccak_squeeze(struct KeccakContext *ctx, uint8_t *dst, size_t len); /** * Extract bytes from state, XOR into data. */ void keccak_squeeze_xor(struct KeccakContext *ctx, uint8_t *dst, const void *src, size_t len); /** * XOR data into state and return it. */ void keccak_encrypt(struct KeccakContext *ctx, uint8_t *dst, const void *src, size_t len); /** * XOR state with data and return it. */ void keccak_decrypt(struct KeccakContext *ctx, uint8_t *dst, const void *src, size_t len); /** * Hash pad suffix. */ void keccak_pad(struct KeccakContext *ctx, const void *data, size_t len); /** * Move internal position to start of buffer. * * Useful for PRNG/duplex modes. */ void keccak_rewind(struct KeccakContext *ctx); /** * Clear rate bits. */ void keccak_forget(struct KeccakContext *ctx); #endif pgbouncer-1.7/lib/usual/crypto/sha1.h0000664000175000017500000000264612511203511014513 00000000000000/* * SHA1 implementation based on RFC3174. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * SHA1 implementation. */ #ifndef _USUAL_CRYPTO_SHA1_H_ #define _USUAL_CRYPTO_SHA1_H_ #include /** Block length for SHA1 */ #define SHA1_BLOCK_SIZE 64 /** Result length for SHA1 */ #define SHA1_DIGEST_LENGTH 20 /** SHA1 state */ struct sha1_ctx { uint64_t nbytes; uint32_t a, b, c, d, e; uint32_t buf[SHA1_BLOCK_SIZE / 4]; }; /** Clean state */ void sha1_reset(struct sha1_ctx *ctx); /** Update state with more data */ void sha1_update(struct sha1_ctx *ctx, const void *data, unsigned int len); /** Get final result */ void sha1_final(struct sha1_ctx *ctx, uint8_t *dst); #endif pgbouncer-1.7/lib/usual/crypto/md5.h0000664000175000017500000000264012511203511014336 00000000000000/* * MD5 implementation based on RFC1321. * * Copyright (c) 2008 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * MD5 cryptographic hash. */ #ifndef _USUAL_CRYPTO_MD5_H_ #define _USUAL_CRYPTO_MD5_H_ #include /** Block length for MD5 */ #define MD5_BLOCK_LENGTH 64 /** Result length for MD5 */ #define MD5_DIGEST_LENGTH 16 /** MD5 state */ struct md5_ctx { uint64_t nbytes; uint32_t a, b, c, d; uint32_t buf[16]; }; /** Clean state */ void md5_reset(struct md5_ctx *ctx); /** Update state with more data */ void md5_update(struct md5_ctx *ctx, const void *data, unsigned int len); /** Get final result */ void md5_final(struct md5_ctx *ctx, uint8_t *dst); #endif pgbouncer-1.7/lib/usual/crypto/sha256.c0000664000175000017500000001327412511203511014661 00000000000000/* * SHA2-256 implementation based on FIPS180-2. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include /* repeat with increasing offset */ #define R4(R, t) R(t+0); R(t+1); R(t+2); R(t+3) #define R16(R, t) R4(R, t+0); R4(R, t+4); R4(R, t+8); R4(R, t+12) #define R64(R, t) R16(R, t+0); R16(R, t+16); R16(R, t+32); R16(R, t+48); #define bufpos(ctx) ((ctx)->nbytes & (SHA256_BLOCK_SIZE - 1)) /* * initial values */ static const uint32_t H224[8] = { 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4, }; static const uint32_t H256[8] = { 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, }; /* * constants for mixing */ static const uint32_t K[64] = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, }; /* * mixing */ #define CH(x,y,z) ((x & y) ^ ((~x) & z)) #define MAJ(x,y,z) ((x & y) ^ (x & z) ^ (y & z)) #define E0(x) (ror32(x, 2) ^ ror32(x, 13) ^ ror32(x, 22)) #define E1(x) (ror32(x, 6) ^ ror32(x, 11) ^ ror32(x, 25)) #define O0(x) (ror32(x, 7) ^ ror32(x, 18) ^ (x >> 3)) #define O1(x) (ror32(x, 17) ^ ror32(x, 19) ^ (x >> 10)) #define W(n) (ctx->buf.words[(n) & 15]) #define setW(n,v) W(n) = (v) #define SHA256_ROUND(_t) do { \ uint32_t tmp1, tmp2, t = (_t); \ if (t >= 16) { \ setW(t, O1(W(t - 2)) + W(t - 7) + O0(W(t - 15)) + W(t - 16)); \ } else { \ /* convert endianess on first go */ \ setW(t, be32toh(W(t))); \ } \ tmp1 = h + E1(e) + CH(e,f,g) + K[k_pos++] + W(t); \ tmp2 = E0(a) + MAJ(a,b,c); \ h = g; g = f; f = e; e = d + tmp1; d = c; c = b; b = a; a = tmp1 + tmp2; \ } while (0) /* * actual core */ static void sha256_core(struct sha256_ctx *ctx) { uint32_t *state = ctx->state; uint32_t a = state[0], b = state[1], c = state[2], d = state[3]; uint32_t e = state[4], f = state[5], g = state[6], h = state[7]; unsigned k_pos = 0; R16(SHA256_ROUND, 0); while (k_pos < 64) { R16(SHA256_ROUND, 16); } state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; state[5] += f; state[6] += g; state[7] += h; } /* * Public API for SHA256. */ void sha256_reset(struct sha256_ctx *ctx) { memset(ctx, 0, sizeof(*ctx)); memcpy(ctx->state, H256, sizeof(H256)); } void sha256_update(struct sha256_ctx *ctx, const void *data, unsigned int len) { unsigned int n; const uint8_t *src = data; uint8_t *dst = ctx->buf.raw; while (len > 0) { n = SHA256_BLOCK_SIZE - bufpos(ctx); if (n > len) n = len; memcpy(dst + bufpos(ctx), src, n); src += n; len -= n; ctx->nbytes += n; if (bufpos(ctx) == 0) sha256_core(ctx); } } void sha256_final(struct sha256_ctx *ctx, uint8_t *dst) { static const uint8_t padding[SHA256_BLOCK_SIZE] = { 0x80 }; uint64_t nbits = ctx->nbytes * 8; int pad_len, pos = bufpos(ctx); int i; /* add padding */ pad_len = SHA256_BLOCK_SIZE - 8 - pos; if (pad_len <= 0) pad_len += SHA256_BLOCK_SIZE; sha256_update(ctx, padding, pad_len); /* add length */ ctx->buf.words[14] = htobe32(nbits >> 32); ctx->buf.words[15] = htobe32(nbits); /* final result */ sha256_core(ctx); for (i = 0; i < SHA256_DIGEST_LENGTH / 4; i++) be32enc(dst + i*4, ctx->state[i]); } /* * Public API for SHA224. */ void sha224_reset(struct sha256_ctx *ctx) { memset(ctx, 0, sizeof(*ctx)); memcpy(ctx->state, H224, sizeof(H224)); } void sha224_update(struct sha256_ctx *ctx, const void *data, unsigned int len) { sha256_update(ctx, data, len); } void sha224_final(struct sha256_ctx *ctx, uint8_t *dst) { uint8_t buf[SHA256_DIGEST_LENGTH]; sha256_final(ctx, buf); memcpy(dst, buf, SHA224_DIGEST_LENGTH); memset(buf, 0, sizeof(buf)); } /* * DigestInfo */ const struct DigestInfo *digest_SHA224(void) { static const struct DigestInfo info = { (DigestInitFunc *)sha224_reset, (DigestUpdateFunc *)sha224_update, (DigestFinalFunc *)sha224_final, sizeof(struct sha256_ctx), SHA224_DIGEST_LENGTH, SHA224_BLOCK_SIZE }; return &info; } const struct DigestInfo *digest_SHA256(void) { static const struct DigestInfo info = { (DigestInitFunc *)sha256_reset, (DigestUpdateFunc *)sha256_update, (DigestFinalFunc *)sha256_final, sizeof(struct sha256_ctx), SHA256_DIGEST_LENGTH, SHA256_BLOCK_SIZE }; return &info; } pgbouncer-1.7/lib/usual/crypto/chacha.h0000664000175000017500000000327612511203511015066 00000000000000/* * ChaCha cipher. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * ChaCha cipher. */ #ifndef _CHACHA_CLEAN_H_ #define _CHACHA_CLEAN_H_ #include #define CHACHA_KEY_SIZE 32 #define CHACHA_IV_SIZE 8 #define CHACHA_BLOCK_SIZE 64 /** * ChaCha state. */ struct ChaCha { uint32_t state[16]; union { uint32_t output32[16]; uint8_t output8[16*4]; } u; unsigned int pos; }; /** * Set 256-bit key. */ void chacha_set_key_256(struct ChaCha *ctx, const void *key); /** * Set 128-bit key. */ void chacha_set_key_128(struct ChaCha *ctx, const void *key); /** * Set 2x32-bit counter and 8-byte IV. */ void chacha_set_nonce(struct ChaCha *ctx, uint32_t counter_low, uint32_t counter_high, const void *iv); /** * Extract plain keystream. */ void chacha_keystream(struct ChaCha *ctx, void *stream, size_t bytes); /** * XOR data with keystream. */ void chacha_keystream_xor(struct ChaCha *ctx, const void *plain, void *encrypted, size_t bytes); #endif pgbouncer-1.7/lib/usual/crypto/keccak_prng.h0000664000175000017500000000330512556647420016143 00000000000000/* * PRNG based on Keccak. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Implements PRNG mode for Keccak sponge function. */ #ifndef _USUAL_CRYPTO_KECCAK_PRNG_H_ #define _USUAL_CRYPTO_KECCAK_PRNG_H_ #include /** * State structure. */ struct KeccakPRNG { struct KeccakContext ctx; bool extracting; bool have_data; }; /** * Setup Keccak with specified capacity. * * @param prng State structure to be initialized. * @param capacity Keccak capacity in bits. * @return False if invalid capacity, true otherwise. */ bool keccak_prng_init(struct KeccakPRNG *prng, int capacity); /** * Merge entropy data into state. */ void keccak_prng_add_data(struct KeccakPRNG *prng, const void *data, size_t len); /** * Extract PRNG bytes from state. * * @return True, if extraction was successful. False if state has not been initialzed with keccak_prng_add_data(). */ bool keccak_prng_extract(struct KeccakPRNG *prng, void *data, size_t len); #endif pgbouncer-1.7/lib/usual/crypto/digest.h0000664000175000017500000000617012511203511015132 00000000000000/* * Common API for cryptographic digests. * * Copyright (c) 2012 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Common API for cryptographic digests. */ #ifndef _USUAL_CRYPTO_DIGEST_H_ #define _USUAL_CRYPTO_DIGEST_H_ #include typedef void (DigestInitFunc)(void *ctx); typedef void (DigestUpdateFunc)(void *ctx, const void *, unsigned); typedef void (DigestFinalFunc)(void *ctx, uint8_t *); /** * Algoright info. */ struct DigestInfo { DigestInitFunc *init; DigestUpdateFunc *update; DigestFinalFunc *final; short state_len; short result_len; short block_len; }; /** * Algoright instance. */ struct DigestContext; /** * Allocate and initialize new algorithm instance. */ struct DigestContext *digest_new(const struct DigestInfo *impl, CxMem *cx); /** Hash more data */ void digest_update(struct DigestContext *ctx, const void *data, size_t len); /** * Get final result. * * To re-use same instance, digest_reset() must be called first. */ void digest_final(struct DigestContext *ctx, uint8_t *res); /** * Prepares instance for new data. */ void digest_reset(struct DigestContext *ctx); /** * Free instance. */ void digest_free(struct DigestContext *ctx); /** * Hash function block length in bytes. */ unsigned digest_block_len(struct DigestContext *ctx); /** * Hash function result length in bytes. */ unsigned digest_result_len(struct DigestContext *ctx); /* * Declare algorithm info's here instead per-also headers * to avoid unnecessary dependencies. */ /** MD5 message digest */ const struct DigestInfo *digest_MD5(void); /** SHA1 message digest */ const struct DigestInfo *digest_SHA1(void); /** SHA224 message digest */ const struct DigestInfo *digest_SHA224(void); /** SHA256 message digest */ const struct DigestInfo *digest_SHA256(void); /** SHA384 message digest */ const struct DigestInfo *digest_SHA384(void); /** SHA512 message digest */ const struct DigestInfo *digest_SHA512(void); /** SHA3-224 message digest */ const struct DigestInfo *digest_SHA3_224(void); /** SHA3-256 message digest */ const struct DigestInfo *digest_SHA3_256(void); /** SHA3-384 message digest */ const struct DigestInfo *digest_SHA3_384(void); /** SHA3-512 message digest */ const struct DigestInfo *digest_SHA3_512(void); /** SHAKE128 in regular digest mode */ const struct DigestInfo *digest_SHAKE128(void); /** SHAKE256 in regular digest mode */ const struct DigestInfo *digest_SHAKE256(void); #endif pgbouncer-1.7/lib/usual/crypto/hmac.c0000664000175000017500000000554012565314367014603 00000000000000/* * HMAC implementation based on OpenBSD hmac.c * * Copyright (c) 2012 Daniel Farina * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include struct HMAC { struct DigestContext *hash; CxMem *cx; uint8_t *ipad; uint8_t *opad; }; struct HMAC *hmac_new(const struct DigestInfo *impl, const void *key, unsigned int key_len, CxMem *cx) { struct DigestContext *hash; struct HMAC *hmac; unsigned bs = impl->block_len; unsigned i; /* load hash */ hash = digest_new(impl, cx); if (!hash) return NULL; /* struct setup */ hmac = cx_alloc0(cx, sizeof(struct HMAC) + 2*bs); if (!hmac) { digest_free(hash); return NULL; } hmac->hash = hash; hmac->cx = cx; hmac->ipad = (uint8_t *)(hmac + 1); hmac->opad = hmac->ipad + bs; /* copy key to pads */ if (key_len > bs) { digest_update(hash, key, key_len); digest_final(hash, hmac->ipad); digest_reset(hash); memcpy(hmac->opad, hmac->ipad, digest_result_len(hash)); } else { memcpy(hmac->ipad, key, key_len); memcpy(hmac->opad, key, key_len); } /* calculate pads */ for (i = 0; i < bs; i++) { hmac->ipad[i] ^= 0x36; hmac->opad[i] ^= 0x5c; } /* prepare for user data */ digest_update(hmac->hash, hmac->ipad, bs); return hmac; } /* Free context */ void hmac_free(struct HMAC *ctx) { digest_free(ctx->hash); cx_free(ctx->cx, ctx); } /* Clean HMAC state */ void hmac_reset(struct HMAC *ctx) { unsigned bs = digest_block_len(ctx->hash); digest_reset(ctx->hash); digest_update(ctx->hash, ctx->ipad, bs); } /* Update HMAC state with more data */ void hmac_update(struct HMAC *ctx, const void *data, unsigned int len) { digest_update(ctx->hash, data, len); } /* Get final HMAC result */ void hmac_final(struct HMAC *ctx, uint8_t *dst) { unsigned bs = digest_block_len(ctx->hash); unsigned rs = digest_result_len(ctx->hash); digest_final(ctx->hash, dst); digest_reset(ctx->hash); digest_update(ctx->hash, ctx->opad, bs); digest_update(ctx->hash, dst, rs); digest_final(ctx->hash, dst); } unsigned hmac_block_len(struct HMAC *ctx) { return digest_block_len(ctx->hash); } unsigned hmac_result_len(struct HMAC *ctx) { return digest_result_len(ctx->hash); } pgbouncer-1.7/lib/usual/crypto/sha256.h0000664000175000017500000000372512556647420014712 00000000000000/* * SHA2-256 implementation based on FIPS180-2. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * SHA256 and SHA224 cryptographic hashes. */ #ifndef _USUAL_CRYPTO_SHA256_H_ #define _USUAL_CRYPTO_SHA256_H_ #include /** SHA224 block size in bytes */ #define SHA224_BLOCK_SIZE (16*4) /** SHA256 block size in bytes */ #define SHA256_BLOCK_SIZE (16*4) /** SHA224 result length in bytes */ #define SHA224_DIGEST_LENGTH (224/8) /** SHA256 result length in bytes */ #define SHA256_DIGEST_LENGTH (256/8) /** * State structure for both SHA256 and SHA224. */ struct sha256_ctx { union { uint32_t words[16]; uint8_t raw[16 * 4]; } buf; uint32_t state[8]; uint64_t nbytes; }; /** Initialize structure for SHA256 */ void sha256_reset(struct sha256_ctx *ctx); /** Process more data */ void sha256_update(struct sha256_ctx *ctx, const void *data, unsigned int len); /** Calculate final result */ void sha256_final(struct sha256_ctx *ctx, uint8_t *dst); /** Initialize structure for SHA224 */ void sha224_reset(struct sha256_ctx *ctx); /** Process more data */ void sha224_update(struct sha256_ctx *ctx, const void *data, unsigned int len); /** Calculate final result */ void sha224_final(struct sha256_ctx *ctx, uint8_t *dst); #endif pgbouncer-1.7/lib/usual/crypto/sha512.h0000664000175000017500000000372512556647420014705 00000000000000/* * SHA2-512 implementation based on FIPS180-2. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * SHA512 and SHA384 cryptographic hashes. */ #ifndef _USUAL_CRYPTO_SHA512_H_ #define _USUAL_CRYPTO_SHA512_H_ #include /** SHA384 block size in bytes */ #define SHA384_BLOCK_SIZE (16*8) /** SHA512 block size in bytes */ #define SHA512_BLOCK_SIZE (16*8) /** SHA384 result length in bytes */ #define SHA384_DIGEST_LENGTH (384/8) /** SHA512 result length in bytes */ #define SHA512_DIGEST_LENGTH (512/8) /** * State structure for both SHA512 and SHA384. */ struct sha512_ctx { union { uint64_t words[16]; uint8_t raw[16 * 8]; } buf; uint64_t state[8]; uint64_t nbytes; }; /** Initialize structure for SHA512 */ void sha512_reset(struct sha512_ctx *ctx); /** Process more data */ void sha512_update(struct sha512_ctx *ctx, const void *data, unsigned int len); /** Calculate final result */ void sha512_final(struct sha512_ctx *ctx, uint8_t *dst); /** Initialize structure for SHA384 */ void sha384_reset(struct sha512_ctx *ctx); /** Process more data */ void sha384_update(struct sha512_ctx *ctx, const void *data, unsigned int len); /** Calculate final result */ void sha384_final(struct sha512_ctx *ctx, uint8_t *dst); #endif pgbouncer-1.7/lib/usual/crypto/keccak_prng.c0000664000175000017500000000304112556647420016133 00000000000000/* * PRNG based on Keccak. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include bool keccak_prng_init(struct KeccakPRNG *prng, int capacity) { if (!keccak_init(&prng->ctx, capacity)) return false; prng->extracting = false; prng->have_data = false; return true; } void keccak_prng_add_data(struct KeccakPRNG *prng, const void *data, size_t len) { if (prng->extracting) { keccak_rewind(&prng->ctx); prng->extracting = false; } keccak_absorb(&prng->ctx, data, len); if (!prng->have_data && len > 0) prng->have_data = true; } bool keccak_prng_extract(struct KeccakPRNG *prng, void *data, size_t len) { if (!prng->have_data) return false; if (!prng->extracting) { keccak_pad(&prng->ctx, "\x01", 1); prng->extracting = true; } keccak_squeeze(&prng->ctx, data, len); return true; } pgbouncer-1.7/lib/usual/crypto/sha512.c0000664000175000017500000001705312511203511014653 00000000000000/* * SHA2-512 implementation based on FIPS180-2. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include /* repeat with increasing offset */ #define R4(R, t) R(t+0); R(t+1); R(t+2); R(t+3) #define R16(R, t) R4(R, t+0); R4(R, t+4); R4(R, t+8); R4(R, t+12) #define R64(R, t) R16(R, t+0); R16(R, t+16); R16(R, t+32); R16(R, t+48); #define bufpos(ctx) ((ctx)->nbytes & (SHA512_BLOCK_SIZE - 1)) /* * initial values */ static const uint64_t H384[8] = { UINT64_C(0xcbbb9d5dc1059ed8), UINT64_C(0x629a292a367cd507), UINT64_C(0x9159015a3070dd17), UINT64_C(0x152fecd8f70e5939), UINT64_C(0x67332667ffc00b31), UINT64_C(0x8eb44a8768581511), UINT64_C(0xdb0c2e0d64f98fa7), UINT64_C(0x47b5481dbefa4fa4), }; static const uint64_t H512[8] = { UINT64_C(0x6a09e667f3bcc908), UINT64_C(0xbb67ae8584caa73b), UINT64_C(0x3c6ef372fe94f82b), UINT64_C(0xa54ff53a5f1d36f1), UINT64_C(0x510e527fade682d1), UINT64_C(0x9b05688c2b3e6c1f), UINT64_C(0x1f83d9abfb41bd6b), UINT64_C(0x5be0cd19137e2179), }; /* * constants for mixing */ static const uint64_t K[80] = { UINT64_C(0x428a2f98d728ae22), UINT64_C(0x7137449123ef65cd), UINT64_C(0xb5c0fbcfec4d3b2f), UINT64_C(0xe9b5dba58189dbbc), UINT64_C(0x3956c25bf348b538), UINT64_C(0x59f111f1b605d019), UINT64_C(0x923f82a4af194f9b), UINT64_C(0xab1c5ed5da6d8118), UINT64_C(0xd807aa98a3030242), UINT64_C(0x12835b0145706fbe), UINT64_C(0x243185be4ee4b28c), UINT64_C(0x550c7dc3d5ffb4e2), UINT64_C(0x72be5d74f27b896f), UINT64_C(0x80deb1fe3b1696b1), UINT64_C(0x9bdc06a725c71235), UINT64_C(0xc19bf174cf692694), UINT64_C(0xe49b69c19ef14ad2), UINT64_C(0xefbe4786384f25e3), UINT64_C(0x0fc19dc68b8cd5b5), UINT64_C(0x240ca1cc77ac9c65), UINT64_C(0x2de92c6f592b0275), UINT64_C(0x4a7484aa6ea6e483), UINT64_C(0x5cb0a9dcbd41fbd4), UINT64_C(0x76f988da831153b5), UINT64_C(0x983e5152ee66dfab), UINT64_C(0xa831c66d2db43210), UINT64_C(0xb00327c898fb213f), UINT64_C(0xbf597fc7beef0ee4), UINT64_C(0xc6e00bf33da88fc2), UINT64_C(0xd5a79147930aa725), UINT64_C(0x06ca6351e003826f), UINT64_C(0x142929670a0e6e70), UINT64_C(0x27b70a8546d22ffc), UINT64_C(0x2e1b21385c26c926), UINT64_C(0x4d2c6dfc5ac42aed), UINT64_C(0x53380d139d95b3df), UINT64_C(0x650a73548baf63de), UINT64_C(0x766a0abb3c77b2a8), UINT64_C(0x81c2c92e47edaee6), UINT64_C(0x92722c851482353b), UINT64_C(0xa2bfe8a14cf10364), UINT64_C(0xa81a664bbc423001), UINT64_C(0xc24b8b70d0f89791), UINT64_C(0xc76c51a30654be30), UINT64_C(0xd192e819d6ef5218), UINT64_C(0xd69906245565a910), UINT64_C(0xf40e35855771202a), UINT64_C(0x106aa07032bbd1b8), UINT64_C(0x19a4c116b8d2d0c8), UINT64_C(0x1e376c085141ab53), UINT64_C(0x2748774cdf8eeb99), UINT64_C(0x34b0bcb5e19b48a8), UINT64_C(0x391c0cb3c5c95a63), UINT64_C(0x4ed8aa4ae3418acb), UINT64_C(0x5b9cca4f7763e373), UINT64_C(0x682e6ff3d6b2b8a3), UINT64_C(0x748f82ee5defb2fc), UINT64_C(0x78a5636f43172f60), UINT64_C(0x84c87814a1f0ab72), UINT64_C(0x8cc702081a6439ec), UINT64_C(0x90befffa23631e28), UINT64_C(0xa4506cebde82bde9), UINT64_C(0xbef9a3f7b2c67915), UINT64_C(0xc67178f2e372532b), UINT64_C(0xca273eceea26619c), UINT64_C(0xd186b8c721c0c207), UINT64_C(0xeada7dd6cde0eb1e), UINT64_C(0xf57d4f7fee6ed178), UINT64_C(0x06f067aa72176fba), UINT64_C(0x0a637dc5a2c898a6), UINT64_C(0x113f9804bef90dae), UINT64_C(0x1b710b35131c471b), UINT64_C(0x28db77f523047d84), UINT64_C(0x32caab7b40c72493), UINT64_C(0x3c9ebe0a15c9bebc), UINT64_C(0x431d67c49c100d4c), UINT64_C(0x4cc5d4becb3e42b6), UINT64_C(0x597f299cfc657e2a), UINT64_C(0x5fcb6fab3ad6faec), UINT64_C(0x6c44198c4a475817), }; /* * mixing */ #define CH(x,y,z) ((x & y) ^ ((~x) & z)) #define MAJ(x,y,z) ((x & y) ^ (x & z) ^ (y & z)) #define E0(x) (ror64(x, 28) ^ ror64(x, 34) ^ ror64(x, 39)) #define E1(x) (ror64(x, 14) ^ ror64(x, 18) ^ ror64(x, 41)) #define O0(x) (ror64(x, 1) ^ ror64(x, 8) ^ (x >> 7)) #define O1(x) (ror64(x, 19) ^ ror64(x, 61) ^ (x >> 6)) #define W(n) (ctx->buf.words[(n) & 15]) #define setW(n,v) W(n) = (v) #define SHA512_ROUND(_t) do { \ uint64_t tmp1, tmp2, t = (_t); \ if (t >= 16) { \ setW(t, O1(W(t - 2)) + W(t - 7) + O0(W(t - 15)) + W(t - 16)); \ } else { \ /* convert endianess on first go */ \ setW(t, be64toh(W(t))); \ } \ tmp1 = h + E1(e) + CH(e,f,g) + K[k_pos++] + W(t); \ tmp2 = E0(a) + MAJ(a,b,c); \ h = g; g = f; f = e; e = d + tmp1; d = c; c = b; b = a; a = tmp1 + tmp2; \ } while (0) /* * actual core */ static void sha512_core(struct sha512_ctx *ctx) { uint64_t *state = ctx->state; uint64_t a = state[0], b = state[1], c = state[2], d = state[3]; uint64_t e = state[4], f = state[5], g = state[6], h = state[7]; unsigned k_pos = 0; R16(SHA512_ROUND, 0); while (k_pos < 80) { R16(SHA512_ROUND, 16); } state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; state[5] += f; state[6] += g; state[7] += h; } /* * Public API for SHA512. */ void sha512_reset(struct sha512_ctx *ctx) { memset(ctx, 0, sizeof(*ctx)); memcpy(ctx->state, H512, sizeof(H512)); } void sha512_update(struct sha512_ctx *ctx, const void *data, unsigned int len) { unsigned int n; const uint8_t *src = data; uint8_t *dst = ctx->buf.raw; while (len > 0) { n = SHA512_BLOCK_SIZE - bufpos(ctx); if (n > len) n = len; memcpy(dst + bufpos(ctx), src, n); src += n; len -= n; ctx->nbytes += n; if (bufpos(ctx) == 0) sha512_core(ctx); } } void sha512_final(struct sha512_ctx *ctx, uint8_t *dst) { static const uint8_t padding[SHA512_BLOCK_SIZE] = { 0x80 }; uint64_t nbits = ctx->nbytes * 8; int i, pad_len; /* add padding */ pad_len = SHA512_BLOCK_SIZE - 16 - bufpos(ctx); if (pad_len <= 0) pad_len += SHA512_BLOCK_SIZE; sha512_update(ctx, padding, pad_len); /* add length */ ctx->buf.words[14] = 0; ctx->buf.words[15] = htobe64(nbits); /* final result */ sha512_core(ctx); for (i = 0; i < SHA512_DIGEST_LENGTH / 8; i++) be64enc(dst + i*8, ctx->state[i]); } /* * Public API for SHA384. */ void sha384_reset(struct sha512_ctx *ctx) { memset(ctx, 0, sizeof(*ctx)); memcpy(ctx->state, H384, sizeof(H384)); } void sha384_update(struct sha512_ctx *ctx, const void *data, unsigned int len) { sha512_update(ctx, data, len); } void sha384_final(struct sha512_ctx *ctx, uint8_t *dst) { uint8_t buf[SHA512_DIGEST_LENGTH]; sha512_final(ctx, buf); memcpy(dst, buf, SHA384_DIGEST_LENGTH); memset(buf, 0, sizeof(buf)); } /* * DigestInfo */ const struct DigestInfo *digest_SHA384(void) { static const struct DigestInfo info = { (DigestInitFunc *)sha384_reset, (DigestUpdateFunc *)sha384_update, (DigestFinalFunc *)sha384_final, sizeof(struct sha512_ctx), SHA384_DIGEST_LENGTH, SHA384_BLOCK_SIZE }; return &info; } const struct DigestInfo *digest_SHA512(void) { static const struct DigestInfo info = { (DigestInitFunc *)sha512_reset, (DigestUpdateFunc *)sha512_update, (DigestFinalFunc *)sha512_final, sizeof(struct sha512_ctx), SHA512_DIGEST_LENGTH, SHA512_BLOCK_SIZE }; return &info; } pgbouncer-1.7/lib/usual/crypto/hmac.h0000664000175000017500000000303012565314367014600 00000000000000/* * HMAC implementation based on OpenBSD * * Copyright (c) 2012 Daniel Farina * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * HMAC-SHA1 implementation (RFC2104). */ #ifndef _USUAL_CRYPTO_HMAC_H_ #define _USUAL_CRYPTO_HMAC_H_ #include /** HMAC Context */ struct HMAC; /** Create context with key */ struct HMAC *hmac_new(const struct DigestInfo *impl, const void *key, unsigned int key_len, CxMem *cx); /** Free context */ void hmac_free(struct HMAC *ctx); /** Initialize context */ void hmac_reset(struct HMAC *ctx); /** Hash more data */ void hmac_update(struct HMAC *ctx, const void *data, unsigned int len); /** Get final result */ void hmac_final(struct HMAC *ctx, uint8_t *dst); unsigned hmac_block_len(struct HMAC *ctx); unsigned hmac_result_len(struct HMAC *ctx); #endif /* _USUAL_HMAC_H_ */ pgbouncer-1.7/lib/usual/crypto/entropy.c0000664000175000017500000000671412511203511015352 00000000000000/* * Load entropy. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #ifndef HAVE_GETENTROPY /* * Load system entropy. */ #if defined(_WIN32) || defined(_WIN64) /* * Windows * * It's possible to get entropy via: * - CryptGenRandom. Uses RtlGenRandom, requires CryptoAPI. * - rand_s(). Uses RtlGenRandom, Requires VS2005 CRT, WindowsXP+. * Missing in mingw32, exists in mingw64. * - RtlGenRandom(). Internal func, no proper public definition. * There is broken def in that does not have NTAPI. * Need to link or load from advapi32.dll. */ typedef BOOLEAN APIENTRY (*rtlgenrandom_t)(void *, ULONG); int getentropy(void *dst, size_t len) { HMODULE lib; rtlgenrandom_t fn; int res = -1; lib = LoadLibrary("advapi32.dll"); if (lib) { fn = (rtlgenrandom_t)GetProcAddress(lib, "SystemFunction036"); if (fn && fn(dst, len)) res = 0; FreeLibrary(lib); } if (res < 0) errno = EIO; return res; } #elif defined(HAVE_GETRANDOM) int getentropy(void *dst, size_t len) { int res; if (len > 256) goto eio; res = getrandom(dst, len, 0); if (res < 0) return -1; if (res == len) return 0; eio: errno = EIO; return -1; } #else /* UNIX-like system */ #include #include /* open and check device node */ static int open_devrandom(const char *dev) { int fd; int oflags = O_RDONLY; #ifdef O_CLOEXEC oflags |= O_CLOEXEC; #endif open_loop: fd = open(dev, oflags); if (fd == -1) { if (errno == EINTR) goto open_loop; return -1; } #ifndef O_CLOEXEC fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); #endif /* * Lightly verify that the device node looks sane */ { struct stat st; if (fstat(fd, &st) == -1 || !S_ISCHR(st.st_mode)) goto fail; } #ifdef RNDGETENTCNT { int cnt; if (ioctl(fd, RNDGETENTCNT, &cnt) == -1) goto fail; } #endif /* seems fine */ return fd; fail: close(fd); return -1; } /* * Read normal random devices under /dev. */ static const char *devlist[] = { "/dev/urandom", "/dev/random", NULL, }; int getentropy(void *dst, size_t bytes) { uint8_t *d = dst; size_t need = bytes; int fd, res; unsigned int i; for (i = 0; devlist[i]; i++) { reopen: fd = open_devrandom(devlist[i]); if (fd == -1) continue; while (need > 0) { res = read(fd, d, need); if (res > 0) { /* successful read */ need -= res; d += res; } else if (res == 0) { /* eof - open again */ close(fd); goto reopen; } else if (errno == EINTR) { /* signal - retry read */ } else { close(fd); /* random error, fail */ return -1; } } close(fd); return 0; } errno = EIO; return -1; } #endif /* unix */ #endif /* !HAVE_GETENTROPY */ pgbouncer-1.7/lib/usual/crypto/csrandom.h0000664000175000017500000000231012511203511015451 00000000000000/* * Cryptographically Secure Randomness. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Cryptographically Secure Randomness. */ #ifndef _USUAL_CRYPTO_CSRANDOM_H_ #define _USUAL_CRYPTO_CSRANDOM_H_ #include /** * Return random uint32_t. */ uint32_t csrandom(void); /** * Return unsigned integer in range. */ uint32_t csrandom_range(uint32_t upper_bound); /** * Fill buffer with random bytes. */ void csrandom_bytes(void *buf, size_t nbytes); #endif pgbouncer-1.7/lib/usual/crypto/csrandom.c0000664000175000017500000000644712557744277015520 00000000000000/* * Cryptographically Secure Randomness. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #ifdef HAVE_ARC4RANDOM_BUF /* * Simply wrap arc4random_buf() API. */ uint32_t csrandom(void) { return arc4random(); } void csrandom_bytes(void *buf, size_t nbytes) { arc4random_buf(buf, nbytes); } uint32_t csrandom_range(uint32_t upper_bound) { return arc4random_uniform(upper_bound); } #else /* !HAVE_ARC4RANDOM_BUF */ #define USE_KECCAK #ifdef USE_KECCAK /* * Keccak-based PRNG. */ static struct KeccakPRNG prng_keccak; static void impl_init(void) { char buf[32]; if (getentropy(buf, sizeof(buf)) != 0) errx(1, "Cannot get system entropy"); if (!keccak_prng_init(&prng_keccak, 576)) errx(1, "Cannot initialize PRNG"); keccak_prng_add_data(&prng_keccak, buf, sizeof(buf)); explicit_bzero(buf, sizeof(buf)); } static void impl_extract(void *buf, size_t nbytes) { keccak_prng_extract(&prng_keccak, buf, nbytes); } #else /* * ChaCha-based PRNG. */ static struct ChaCha prng_chacha; static void impl_init(void) { uint8_t buf[CHACHA_KEY_SIZE + CHACHA_IV_SIZE]; if (getentropy(buf, sizeof(buf)) != 0) errx(1, "Cannot get system entropy"); chacha_set_key_256(&prng_chacha, buf); chacha_set_nonce(&prng_chacha, 0, 0, buf + CHACHA_KEY_SIZE); explicit_bzero(buf, sizeof(buf)); } static void impl_extract(void *buf, size_t nbytes) { chacha_keystream(&prng_chacha, buf, nbytes); } #endif /* * Locking */ static pid_t last_pid = -1; static void prng_lock(void) { } static void prng_unlock(void) { } /* * Make sure state is initialized. */ static void prng_check_and_lock(void) { bool reseed = false; pid_t new_pid; prng_lock(); new_pid = getpid(); if (new_pid != last_pid) { reseed = true; last_pid = new_pid; } if (reseed) impl_init(); } /* * Public API follows */ void csrandom_bytes(void *buf, size_t nbytes) { prng_check_and_lock(); impl_extract(buf, nbytes); prng_unlock(); } uint32_t csrandom(void) { uint32_t val; csrandom_bytes(&val, sizeof(val)); return val; } uint32_t csrandom_range(uint32_t upper_bound) { uint32_t mod, lim, val; if (upper_bound <= 1) return 0; /* 2**32 % x == (2**32 - x) % x */ mod = -upper_bound % upper_bound; /* wait for value in range [0 .. 2**32-mod) */ lim = -mod; /* loop until good value appears */ while (1) { val = csrandom(); if (val < lim || lim == 0) return val % upper_bound; } } #endif /* !HAVE_ARC4RANDOM_BUF */ pgbouncer-1.7/lib/usual/crypto/keccak.c0000664000175000017500000010530412511203511015066 00000000000000/* * Keccak implementation for SHA3 parameters. * * Copyright (c) 2012 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Based on public-domain Keccak-inplace.c and Keccak-inplace32BI.c * implementations from Keccak reference code: * * The Keccak sponge function, designed by Guido Bertoni, Joan Daemen, * Michaël Peeters and Gilles Van Assche. For more information, feedback or * questions, please refer to our website: http://keccak.noekeon.org/ * * Implementation by Ronny Van Keer and the designers, * hereby denoted as "the implementer". * * To the extent possible under law, the implementer has waived all copyright * and related or neighboring rights to the source code in this file. * http://creativecommons.org/publicdomain/zero/1.0/ * * 32-bit word interlacing algorithm: * * Henry S. Warren, Hacker's Delight, Addison-Wesley, 2002 */ #include #include #include #include #include /* For SHA3 variant of Keccak */ #define KECCAK_ROUNDS 24 /* * Enforce minimal code size. If this is not defined, use * faster unrolled implementation. */ /* #define KECCAK_SMALL */ #ifdef KECCAK_SMALL #define KECCAK_64BIT #endif /* * Decide whether to use 64- or 32-bit implementation. */ #if !defined(KECCAK_64BIT) && !defined(KECCAK_32BIT) #if !defined(LONG_MAX) && !defined(UINTPTR_MAX) #error "Need LONG_MAX & UINTPTR_MAX" #endif /* If neither is defined, try to autodetect */ #if (LONG_MAX > 0xFFFFFFFF) || (UINTPTR_MAX > 0xFFFFFFFF) /* use 64-bit implementation if 'long' or 'uintptr_t' is 64-bit */ #define KECCAK_64BIT #else /* otherwise, use 32-bit implementation */ #define KECCAK_32BIT #endif #endif #ifdef KECCAK_64BIT /* * 64-bit implementation - one lane is one 64-bit word. */ /* round constants */ static const uint64_t RoundConstants64[KECCAK_ROUNDS] = { UINT64_C(0x0000000000000001), UINT64_C(0x0000000000008082), UINT64_C(0x800000000000808A), UINT64_C(0x8000000080008000), UINT64_C(0x000000000000808B), UINT64_C(0x0000000080000001), UINT64_C(0x8000000080008081), UINT64_C(0x8000000000008009), UINT64_C(0x000000000000008A), UINT64_C(0x0000000000000088), UINT64_C(0x0000000080008009), UINT64_C(0x000000008000000A), UINT64_C(0x000000008000808B), UINT64_C(0x800000000000008B), UINT64_C(0x8000000000008089), UINT64_C(0x8000000000008003), UINT64_C(0x8000000000008002), UINT64_C(0x8000000000000080), UINT64_C(0x000000000000800A), UINT64_C(0x800000008000000A), UINT64_C(0x8000000080008081), UINT64_C(0x8000000000008080), UINT64_C(0x0000000080000001), UINT64_C(0x8000000080008008), }; #ifdef KECCAK_SMALL /* * Minimal code implementation */ static const uint8_t RhoRot[24] = { 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44 }; static const uint8_t PiLane[24] = { 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1 }; static void keccak_f(struct KeccakContext *ctx) { int i, j; uint64_t *A = ctx->u.state64; uint64_t tmpbuf[5 + 2], *tmp = tmpbuf + 1; uint64_t d, c1, c2; for (j = 0; j < KECCAK_ROUNDS; j++) { /* Theta step */ for (i = 0; i < 5; i++) tmp[i] = A[0*5 + i] ^ A[1*5 + i] ^ A[2*5 + i] ^ A[3*5 + i] ^ A[4*5 + i]; tmpbuf[0] = tmp[4]; tmpbuf[6] = tmp[0]; for (i = 0; i < 5; i++) { d = tmp[i-1] ^ rol64(tmp[i+1], 1); A[0 + i] ^= d; A[5 + i] ^= d; A[10 + i] ^= d; A[15 + i] ^= d; A[20 + i] ^= d; } /* Rho + Pi step */ c1 = A[PiLane[23]]; for (i = 0; i < 24; i++) { c2 = A[PiLane[i]]; A[PiLane[i]] = rol64(c1, RhoRot[i]); c1 = c2; } /* Chi step */ for (i = 0; i < 25; ) { tmp[0] = A[i+0]; tmp[1] = A[i+1]; A[i] ^= ~A[i+1] & A[i+2]; i++; A[i] ^= ~A[i+1] & A[i+2]; i++; A[i] ^= ~A[i+1] & A[i+2]; i++; A[i] ^= ~A[i+1] & tmp[0]; i++; A[i] ^= ~tmp[0] & tmp[1]; i++; } /* Iota step */ A[0] ^= RoundConstants64[j]; } } #else /* !KECCAK_SMALL - fast 64-bit */ static void keccak_f(struct KeccakContext *ctx) { uint64_t *state = ctx->u.state64; uint64_t Ba, Be, Bi, Bo, Bu; uint64_t Ca, Ce, Ci, Co, Cu; uint64_t Da, De, Di, Do, Du; int i; #define Aba state[ 0] #define Abe state[ 1] #define Abi state[ 2] #define Abo state[ 3] #define Abu state[ 4] #define Aga state[ 5] #define Age state[ 6] #define Agi state[ 7] #define Ago state[ 8] #define Agu state[ 9] #define Aka state[10] #define Ake state[11] #define Aki state[12] #define Ako state[13] #define Aku state[14] #define Ama state[15] #define Ame state[16] #define Ami state[17] #define Amo state[18] #define Amu state[19] #define Asa state[20] #define Ase state[21] #define Asi state[22] #define Aso state[23] #define Asu state[24] for (i = 0; i < KECCAK_ROUNDS; i += 4) { /* Code for 4 rounds */ Ca = Aba^Aga^Aka^Ama^Asa; Ce = Abe^Age^Ake^Ame^Ase; Ci = Abi^Agi^Aki^Ami^Asi; Co = Abo^Ago^Ako^Amo^Aso; Cu = Abu^Agu^Aku^Amu^Asu; Da = Cu^rol64(Ce, 1); De = Ca^rol64(Ci, 1); Di = Ce^rol64(Co, 1); Do = Ci^rol64(Cu, 1); Du = Co^rol64(Ca, 1); Ba = (Aba^Da); Be = rol64((Age^De), 44); Bi = rol64((Aki^Di), 43); Bo = rol64((Amo^Do), 21); Bu = rol64((Asu^Du), 14); Aba = Ba ^((~Be)& Bi ); Aba ^= RoundConstants64[i+0]; Age = Be ^((~Bi)& Bo ); Aki = Bi ^((~Bo)& Bu ); Amo = Bo ^((~Bu)& Ba ); Asu = Bu ^((~Ba)& Be ); Bi = rol64((Aka^Da), 3); Bo = rol64((Ame^De), 45); Bu = rol64((Asi^Di), 61); Ba = rol64((Abo^Do), 28); Be = rol64((Agu^Du), 20); Aka = Ba ^((~Be)& Bi ); Ame = Be ^((~Bi)& Bo ); Asi = Bi ^((~Bo)& Bu ); Abo = Bo ^((~Bu)& Ba ); Agu = Bu ^((~Ba)& Be ); Bu = rol64((Asa^Da), 18); Ba = rol64((Abe^De), 1); Be = rol64((Agi^Di), 6); Bi = rol64((Ako^Do), 25); Bo = rol64((Amu^Du), 8); Asa = Ba ^((~Be)& Bi ); Abe = Be ^((~Bi)& Bo ); Agi = Bi ^((~Bo)& Bu ); Ako = Bo ^((~Bu)& Ba ); Amu = Bu ^((~Ba)& Be ); Be = rol64((Aga^Da), 36); Bi = rol64((Ake^De), 10); Bo = rol64((Ami^Di), 15); Bu = rol64((Aso^Do), 56); Ba = rol64((Abu^Du), 27); Aga = Ba ^((~Be)& Bi ); Ake = Be ^((~Bi)& Bo ); Ami = Bi ^((~Bo)& Bu ); Aso = Bo ^((~Bu)& Ba ); Abu = Bu ^((~Ba)& Be ); Bo = rol64((Ama^Da), 41); Bu = rol64((Ase^De), 2); Ba = rol64((Abi^Di), 62); Be = rol64((Ago^Do), 55); Bi = rol64((Aku^Du), 39); Ama = Ba ^((~Be)& Bi ); Ase = Be ^((~Bi)& Bo ); Abi = Bi ^((~Bo)& Bu ); Ago = Bo ^((~Bu)& Ba ); Aku = Bu ^((~Ba)& Be ); Ca = Aba^Aka^Asa^Aga^Ama; Ce = Age^Ame^Abe^Ake^Ase; Ci = Aki^Asi^Agi^Ami^Abi; Co = Amo^Abo^Ako^Aso^Ago; Cu = Asu^Agu^Amu^Abu^Aku; Da = Cu^rol64(Ce, 1); De = Ca^rol64(Ci, 1); Di = Ce^rol64(Co, 1); Do = Ci^rol64(Cu, 1); Du = Co^rol64(Ca, 1); Ba = (Aba^Da); Be = rol64((Ame^De), 44); Bi = rol64((Agi^Di), 43); Bo = rol64((Aso^Do), 21); Bu = rol64((Aku^Du), 14); Aba = Ba ^((~Be)& Bi ); Aba ^= RoundConstants64[i+1]; Ame = Be ^((~Bi)& Bo ); Agi = Bi ^((~Bo)& Bu ); Aso = Bo ^((~Bu)& Ba ); Aku = Bu ^((~Ba)& Be ); Bi = rol64((Asa^Da), 3); Bo = rol64((Ake^De), 45); Bu = rol64((Abi^Di), 61); Ba = rol64((Amo^Do), 28); Be = rol64((Agu^Du), 20); Asa = Ba ^((~Be)& Bi ); Ake = Be ^((~Bi)& Bo ); Abi = Bi ^((~Bo)& Bu ); Amo = Bo ^((~Bu)& Ba ); Agu = Bu ^((~Ba)& Be ); Bu = rol64((Ama^Da), 18); Ba = rol64((Age^De), 1); Be = rol64((Asi^Di), 6); Bi = rol64((Ako^Do), 25); Bo = rol64((Abu^Du), 8); Ama = Ba ^((~Be)& Bi ); Age = Be ^((~Bi)& Bo ); Asi = Bi ^((~Bo)& Bu ); Ako = Bo ^((~Bu)& Ba ); Abu = Bu ^((~Ba)& Be ); Be = rol64((Aka^Da), 36); Bi = rol64((Abe^De), 10); Bo = rol64((Ami^Di), 15); Bu = rol64((Ago^Do), 56); Ba = rol64((Asu^Du), 27); Aka = Ba ^((~Be)& Bi ); Abe = Be ^((~Bi)& Bo ); Ami = Bi ^((~Bo)& Bu ); Ago = Bo ^((~Bu)& Ba ); Asu = Bu ^((~Ba)& Be ); Bo = rol64((Aga^Da), 41); Bu = rol64((Ase^De), 2); Ba = rol64((Aki^Di), 62); Be = rol64((Abo^Do), 55); Bi = rol64((Amu^Du), 39); Aga = Ba ^((~Be)& Bi ); Ase = Be ^((~Bi)& Bo ); Aki = Bi ^((~Bo)& Bu ); Abo = Bo ^((~Bu)& Ba ); Amu = Bu ^((~Ba)& Be ); Ca = Aba^Asa^Ama^Aka^Aga; Ce = Ame^Ake^Age^Abe^Ase; Ci = Agi^Abi^Asi^Ami^Aki; Co = Aso^Amo^Ako^Ago^Abo; Cu = Aku^Agu^Abu^Asu^Amu; Da = Cu^rol64(Ce, 1); De = Ca^rol64(Ci, 1); Di = Ce^rol64(Co, 1); Do = Ci^rol64(Cu, 1); Du = Co^rol64(Ca, 1); Ba = (Aba^Da); Be = rol64((Ake^De), 44); Bi = rol64((Asi^Di), 43); Bo = rol64((Ago^Do), 21); Bu = rol64((Amu^Du), 14); Aba = Ba ^((~Be)& Bi ); Aba ^= RoundConstants64[i+2]; Ake = Be ^((~Bi)& Bo ); Asi = Bi ^((~Bo)& Bu ); Ago = Bo ^((~Bu)& Ba ); Amu = Bu ^((~Ba)& Be ); Bi = rol64((Ama^Da), 3); Bo = rol64((Abe^De), 45); Bu = rol64((Aki^Di), 61); Ba = rol64((Aso^Do), 28); Be = rol64((Agu^Du), 20); Ama = Ba ^((~Be)& Bi ); Abe = Be ^((~Bi)& Bo ); Aki = Bi ^((~Bo)& Bu ); Aso = Bo ^((~Bu)& Ba ); Agu = Bu ^((~Ba)& Be ); Bu = rol64((Aga^Da), 18); Ba = rol64((Ame^De), 1); Be = rol64((Abi^Di), 6); Bi = rol64((Ako^Do), 25); Bo = rol64((Asu^Du), 8); Aga = Ba ^((~Be)& Bi ); Ame = Be ^((~Bi)& Bo ); Abi = Bi ^((~Bo)& Bu ); Ako = Bo ^((~Bu)& Ba ); Asu = Bu ^((~Ba)& Be ); Be = rol64((Asa^Da), 36); Bi = rol64((Age^De), 10); Bo = rol64((Ami^Di), 15); Bu = rol64((Abo^Do), 56); Ba = rol64((Aku^Du), 27); Asa = Ba ^((~Be)& Bi ); Age = Be ^((~Bi)& Bo ); Ami = Bi ^((~Bo)& Bu ); Abo = Bo ^((~Bu)& Ba ); Aku = Bu ^((~Ba)& Be ); Bo = rol64((Aka^Da), 41); Bu = rol64((Ase^De), 2); Ba = rol64((Agi^Di), 62); Be = rol64((Amo^Do), 55); Bi = rol64((Abu^Du), 39); Aka = Ba ^((~Be)& Bi ); Ase = Be ^((~Bi)& Bo ); Agi = Bi ^((~Bo)& Bu ); Amo = Bo ^((~Bu)& Ba ); Abu = Bu ^((~Ba)& Be ); Ca = Aba^Ama^Aga^Asa^Aka; Ce = Ake^Abe^Ame^Age^Ase; Ci = Asi^Aki^Abi^Ami^Agi; Co = Ago^Aso^Ako^Abo^Amo; Cu = Amu^Agu^Asu^Aku^Abu; Da = Cu^rol64(Ce, 1); De = Ca^rol64(Ci, 1); Di = Ce^rol64(Co, 1); Do = Ci^rol64(Cu, 1); Du = Co^rol64(Ca, 1); Ba = (Aba^Da); Be = rol64((Abe^De), 44); Bi = rol64((Abi^Di), 43); Bo = rol64((Abo^Do), 21); Bu = rol64((Abu^Du), 14); Aba = Ba ^((~Be)& Bi ); Aba ^= RoundConstants64[i+3]; Abe = Be ^((~Bi)& Bo ); Abi = Bi ^((~Bo)& Bu ); Abo = Bo ^((~Bu)& Ba ); Abu = Bu ^((~Ba)& Be ); Bi = rol64((Aga^Da), 3); Bo = rol64((Age^De), 45); Bu = rol64((Agi^Di), 61); Ba = rol64((Ago^Do), 28); Be = rol64((Agu^Du), 20); Aga = Ba ^((~Be)& Bi ); Age = Be ^((~Bi)& Bo ); Agi = Bi ^((~Bo)& Bu ); Ago = Bo ^((~Bu)& Ba ); Agu = Bu ^((~Ba)& Be ); Bu = rol64((Aka^Da), 18); Ba = rol64((Ake^De), 1); Be = rol64((Aki^Di), 6); Bi = rol64((Ako^Do), 25); Bo = rol64((Aku^Du), 8); Aka = Ba ^((~Be)& Bi ); Ake = Be ^((~Bi)& Bo ); Aki = Bi ^((~Bo)& Bu ); Ako = Bo ^((~Bu)& Ba ); Aku = Bu ^((~Ba)& Be ); Be = rol64((Ama^Da), 36); Bi = rol64((Ame^De), 10); Bo = rol64((Ami^Di), 15); Bu = rol64((Amo^Do), 56); Ba = rol64((Amu^Du), 27); Ama = Ba ^((~Be)& Bi ); Ame = Be ^((~Bi)& Bo ); Ami = Bi ^((~Bo)& Bu ); Amo = Bo ^((~Bu)& Ba ); Amu = Bu ^((~Ba)& Be ); Bo = rol64((Asa^Da), 41); Bu = rol64((Ase^De), 2); Ba = rol64((Asi^Di), 62); Be = rol64((Aso^Do), 55); Bi = rol64((Asu^Du), 39); Asa = Ba ^((~Be)& Bi ); Ase = Be ^((~Bi)& Bo ); Asi = Bi ^((~Bo)& Bu ); Aso = Bo ^((~Bu)& Ba ); Asu = Bu ^((~Ba)& Be ); } } #endif /* !KECCAK_SMALL */ static inline void xor_lane(struct KeccakContext *ctx, int lane, uint64_t val) { ctx->u.state64[lane] ^= val; } static void extract(uint8_t *dst, const struct KeccakContext *ctx, int startLane, int laneCount) { const uint64_t *src = ctx->u.state64 + startLane; while (laneCount--) { le64enc(dst, *src++); dst += 8; } } #else /* KECCAK_32BIT */ /* * 32-bit implementation - one 64-bit lane is mapped * to two interleaved 32-bit words. */ static const uint32_t RoundConstants32[2*KECCAK_ROUNDS] = { 0x00000001, 0x00000000, 0x00000000, 0x00000089, 0x00000000, 0x8000008b, 0x00000000, 0x80008080, 0x00000001, 0x0000008b, 0x00000001, 0x00008000, 0x00000001, 0x80008088, 0x00000001, 0x80000082, 0x00000000, 0x0000000b, 0x00000000, 0x0000000a, 0x00000001, 0x00008082, 0x00000000, 0x00008003, 0x00000001, 0x0000808b, 0x00000001, 0x8000000b, 0x00000001, 0x8000008a, 0x00000001, 0x80000081, 0x00000000, 0x80000081, 0x00000000, 0x80000008, 0x00000000, 0x00000083, 0x00000000, 0x80008003, 0x00000001, 0x80008088, 0x00000000, 0x80000088, 0x00000001, 0x00008000, 0x00000000, 0x80008082, }; #define KeccakAtoD_round0() \ Cx = Abu0^Agu0^Aku0^Amu0^Asu0; \ Du1 = Abe1^Age1^Ake1^Ame1^Ase1; \ Da0 = Cx^rol32(Du1, 1); \ Cz = Abu1^Agu1^Aku1^Amu1^Asu1; \ Du0 = Abe0^Age0^Ake0^Ame0^Ase0; \ Da1 = Cz^Du0; \ \ Cw = Abi0^Agi0^Aki0^Ami0^Asi0; \ Do0 = Cw^rol32(Cz, 1); \ Cy = Abi1^Agi1^Aki1^Ami1^Asi1; \ Do1 = Cy^Cx; \ \ Cx = Aba0^Aga0^Aka0^Ama0^Asa0; \ De0 = Cx^rol32(Cy, 1); \ Cz = Aba1^Aga1^Aka1^Ama1^Asa1; \ De1 = Cz^Cw; \ \ Cy = Abo1^Ago1^Ako1^Amo1^Aso1; \ Di0 = Du0^rol32(Cy, 1); \ Cw = Abo0^Ago0^Ako0^Amo0^Aso0; \ Di1 = Du1^Cw; \ \ Du0 = Cw^rol32(Cz, 1); \ Du1 = Cy^Cx; #define KeccakAtoD_round1() \ Cx = Asu0^Agu0^Amu0^Abu1^Aku1; \ Du1 = Age1^Ame0^Abe0^Ake1^Ase1; \ Da0 = Cx^rol32(Du1, 1); \ Cz = Asu1^Agu1^Amu1^Abu0^Aku0; \ Du0 = Age0^Ame1^Abe1^Ake0^Ase0; \ Da1 = Cz^Du0; \ \ Cw = Aki1^Asi1^Agi0^Ami1^Abi0; \ Do0 = Cw^rol32(Cz, 1); \ Cy = Aki0^Asi0^Agi1^Ami0^Abi1; \ Do1 = Cy^Cx; \ \ Cx = Aba0^Aka1^Asa0^Aga0^Ama1; \ De0 = Cx^rol32(Cy, 1); \ Cz = Aba1^Aka0^Asa1^Aga1^Ama0; \ De1 = Cz^Cw; \ \ Cy = Amo0^Abo1^Ako0^Aso1^Ago0; \ Di0 = Du0^rol32(Cy, 1); \ Cw = Amo1^Abo0^Ako1^Aso0^Ago1; \ Di1 = Du1^Cw; \ \ Du0 = Cw^rol32(Cz, 1); \ Du1 = Cy^Cx; #define KeccakAtoD_round2() \ Cx = Aku1^Agu0^Abu1^Asu1^Amu1; \ Du1 = Ame0^Ake0^Age0^Abe0^Ase1; \ Da0 = Cx^rol32(Du1, 1); \ Cz = Aku0^Agu1^Abu0^Asu0^Amu0; \ Du0 = Ame1^Ake1^Age1^Abe1^Ase0; \ Da1 = Cz^Du0; \ \ Cw = Agi1^Abi1^Asi1^Ami0^Aki1; \ Do0 = Cw^rol32(Cz, 1); \ Cy = Agi0^Abi0^Asi0^Ami1^Aki0; \ Do1 = Cy^Cx; \ \ Cx = Aba0^Asa1^Ama1^Aka1^Aga1; \ De0 = Cx^rol32(Cy, 1); \ Cz = Aba1^Asa0^Ama0^Aka0^Aga0; \ De1 = Cz^Cw; \ \ Cy = Aso0^Amo0^Ako1^Ago0^Abo0; \ Di0 = Du0^rol32(Cy, 1); \ Cw = Aso1^Amo1^Ako0^Ago1^Abo1; \ Di1 = Du1^Cw; \ \ Du0 = Cw^rol32(Cz, 1); \ Du1 = Cy^Cx; #define KeccakAtoD_round3() \ Cx = Amu1^Agu0^Asu1^Aku0^Abu0; \ Du1 = Ake0^Abe1^Ame1^Age0^Ase1; \ Da0 = Cx^rol32(Du1, 1); \ Cz = Amu0^Agu1^Asu0^Aku1^Abu1; \ Du0 = Ake1^Abe0^Ame0^Age1^Ase0; \ Da1 = Cz^Du0; \ \ Cw = Asi0^Aki0^Abi1^Ami1^Agi1; \ Do0 = Cw^rol32(Cz, 1); \ Cy = Asi1^Aki1^Abi0^Ami0^Agi0; \ Do1 = Cy^Cx; \ \ Cx = Aba0^Ama0^Aga1^Asa1^Aka0; \ De0 = Cx^rol32(Cy, 1); \ Cz = Aba1^Ama1^Aga0^Asa0^Aka1; \ De1 = Cz^Cw; \ \ Cy = Ago1^Aso0^Ako0^Abo0^Amo1; \ Di0 = Du0^rol32(Cy, 1); \ Cw = Ago0^Aso1^Ako1^Abo1^Amo0; \ Di1 = Du1^Cw; \ \ Du0 = Cw^rol32(Cz, 1); \ Du1 = Cy^Cx; static void keccak_f(struct KeccakContext *ctx) { uint32_t *state = ctx->u.state32; uint32_t Da0, De0, Di0, Do0, Du0; uint32_t Da1, De1, Di1, Do1, Du1; uint32_t Ca0, Ce0, Ci0, Co0, Cu0; uint32_t Cx, Cy, Cz, Cw; int i; #define Ba Ca0 #define Be Ce0 #define Bi Ci0 #define Bo Co0 #define Bu Cu0 #define Aba0 state[ 0] #define Aba1 state[ 1] #define Abe0 state[ 2] #define Abe1 state[ 3] #define Abi0 state[ 4] #define Abi1 state[ 5] #define Abo0 state[ 6] #define Abo1 state[ 7] #define Abu0 state[ 8] #define Abu1 state[ 9] #define Aga0 state[10] #define Aga1 state[11] #define Age0 state[12] #define Age1 state[13] #define Agi0 state[14] #define Agi1 state[15] #define Ago0 state[16] #define Ago1 state[17] #define Agu0 state[18] #define Agu1 state[19] #define Aka0 state[20] #define Aka1 state[21] #define Ake0 state[22] #define Ake1 state[23] #define Aki0 state[24] #define Aki1 state[25] #define Ako0 state[26] #define Ako1 state[27] #define Aku0 state[28] #define Aku1 state[29] #define Ama0 state[30] #define Ama1 state[31] #define Ame0 state[32] #define Ame1 state[33] #define Ami0 state[34] #define Ami1 state[35] #define Amo0 state[36] #define Amo1 state[37] #define Amu0 state[38] #define Amu1 state[39] #define Asa0 state[40] #define Asa1 state[41] #define Ase0 state[42] #define Ase1 state[43] #define Asi0 state[44] #define Asi1 state[45] #define Aso0 state[46] #define Aso1 state[47] #define Asu0 state[48] #define Asu1 state[49] for (i = 0; i < KECCAK_ROUNDS*2; i += 8) { /* Code for 4 rounds */ KeccakAtoD_round0(); Ba = (Aba0^Da0); Be = rol32((Age0^De0), 22); Bi = rol32((Aki1^Di1), 22); Bo = rol32((Amo1^Do1), 11); Bu = rol32((Asu0^Du0), 7); Aba0 = Ba ^((~Be)& Bi ); Aba0 ^= RoundConstants32[i+0]; Age0 = Be ^((~Bi)& Bo ); Aki1 = Bi ^((~Bo)& Bu ); Amo1 = Bo ^((~Bu)& Ba ); Asu0 = Bu ^((~Ba)& Be ); Ba = (Aba1^Da1); Be = rol32((Age1^De1), 22); Bi = rol32((Aki0^Di0), 21); Bo = rol32((Amo0^Do0), 10); Bu = rol32((Asu1^Du1), 7); Aba1 = Ba ^((~Be)& Bi ); Aba1 ^= RoundConstants32[i+1]; Age1 = Be ^((~Bi)& Bo ); Aki0 = Bi ^((~Bo)& Bu ); Amo0 = Bo ^((~Bu)& Ba ); Asu1 = Bu ^((~Ba)& Be ); Bi = rol32((Aka1^Da1), 2); Bo = rol32((Ame1^De1), 23); Bu = rol32((Asi1^Di1), 31); Ba = rol32((Abo0^Do0), 14); Be = rol32((Agu0^Du0), 10); Aka1 = Ba ^((~Be)& Bi ); Ame1 = Be ^((~Bi)& Bo ); Asi1 = Bi ^((~Bo)& Bu ); Abo0 = Bo ^((~Bu)& Ba ); Agu0 = Bu ^((~Ba)& Be ); Bi = rol32((Aka0^Da0), 1); Bo = rol32((Ame0^De0), 22); Bu = rol32((Asi0^Di0), 30); Ba = rol32((Abo1^Do1), 14); Be = rol32((Agu1^Du1), 10); Aka0 = Ba ^((~Be)& Bi ); Ame0 = Be ^((~Bi)& Bo ); Asi0 = Bi ^((~Bo)& Bu ); Abo1 = Bo ^((~Bu)& Ba ); Agu1 = Bu ^((~Ba)& Be ); Bu = rol32((Asa0^Da0), 9); Ba = rol32((Abe1^De1), 1); Be = rol32((Agi0^Di0), 3); Bi = rol32((Ako1^Do1), 13); Bo = rol32((Amu0^Du0), 4); Asa0 = Ba ^((~Be)& Bi ); Abe1 = Be ^((~Bi)& Bo ); Agi0 = Bi ^((~Bo)& Bu ); Ako1 = Bo ^((~Bu)& Ba ); Amu0 = Bu ^((~Ba)& Be ); Bu = rol32((Asa1^Da1), 9); Ba = (Abe0^De0); Be = rol32((Agi1^Di1), 3); Bi = rol32((Ako0^Do0), 12); Bo = rol32((Amu1^Du1), 4); Asa1 = Ba ^((~Be)& Bi ); Abe0 = Be ^((~Bi)& Bo ); Agi1 = Bi ^((~Bo)& Bu ); Ako0 = Bo ^((~Bu)& Ba ); Amu1 = Bu ^((~Ba)& Be ); Be = rol32((Aga0^Da0), 18); Bi = rol32((Ake0^De0), 5); Bo = rol32((Ami1^Di1), 8); Bu = rol32((Aso0^Do0), 28); Ba = rol32((Abu1^Du1), 14); Aga0 = Ba ^((~Be)& Bi ); Ake0 = Be ^((~Bi)& Bo ); Ami1 = Bi ^((~Bo)& Bu ); Aso0 = Bo ^((~Bu)& Ba ); Abu1 = Bu ^((~Ba)& Be ); Be = rol32((Aga1^Da1), 18); Bi = rol32((Ake1^De1), 5); Bo = rol32((Ami0^Di0), 7); Bu = rol32((Aso1^Do1), 28); Ba = rol32((Abu0^Du0), 13); Aga1 = Ba ^((~Be)& Bi ); Ake1 = Be ^((~Bi)& Bo ); Ami0 = Bi ^((~Bo)& Bu ); Aso1 = Bo ^((~Bu)& Ba ); Abu0 = Bu ^((~Ba)& Be ); Bo = rol32((Ama1^Da1), 21); Bu = rol32((Ase0^De0), 1); Ba = rol32((Abi0^Di0), 31); Be = rol32((Ago1^Do1), 28); Bi = rol32((Aku1^Du1), 20); Ama1 = Ba ^((~Be)& Bi ); Ase0 = Be ^((~Bi)& Bo ); Abi0 = Bi ^((~Bo)& Bu ); Ago1 = Bo ^((~Bu)& Ba ); Aku1 = Bu ^((~Ba)& Be ); Bo = rol32((Ama0^Da0), 20); Bu = rol32((Ase1^De1), 1); Ba = rol32((Abi1^Di1), 31); Be = rol32((Ago0^Do0), 27); Bi = rol32((Aku0^Du0), 19); Ama0 = Ba ^((~Be)& Bi ); Ase1 = Be ^((~Bi)& Bo ); Abi1 = Bi ^((~Bo)& Bu ); Ago0 = Bo ^((~Bu)& Ba ); Aku0 = Bu ^((~Ba)& Be ); KeccakAtoD_round1(); Ba = (Aba0^Da0); Be = rol32((Ame1^De0), 22); Bi = rol32((Agi1^Di1), 22); Bo = rol32((Aso1^Do1), 11); Bu = rol32((Aku1^Du0), 7); Aba0 = Ba ^((~Be)& Bi ); Aba0 ^= RoundConstants32[i+2]; Ame1 = Be ^((~Bi)& Bo ); Agi1 = Bi ^((~Bo)& Bu ); Aso1 = Bo ^((~Bu)& Ba ); Aku1 = Bu ^((~Ba)& Be ); Ba = (Aba1^Da1); Be = rol32((Ame0^De1), 22); Bi = rol32((Agi0^Di0), 21); Bo = rol32((Aso0^Do0), 10); Bu = rol32((Aku0^Du1), 7); Aba1 = Ba ^((~Be)& Bi ); Aba1 ^= RoundConstants32[i+3]; Ame0 = Be ^((~Bi)& Bo ); Agi0 = Bi ^((~Bo)& Bu ); Aso0 = Bo ^((~Bu)& Ba ); Aku0 = Bu ^((~Ba)& Be ); Bi = rol32((Asa1^Da1), 2); Bo = rol32((Ake1^De1), 23); Bu = rol32((Abi1^Di1), 31); Ba = rol32((Amo1^Do0), 14); Be = rol32((Agu0^Du0), 10); Asa1 = Ba ^((~Be)& Bi ); Ake1 = Be ^((~Bi)& Bo ); Abi1 = Bi ^((~Bo)& Bu ); Amo1 = Bo ^((~Bu)& Ba ); Agu0 = Bu ^((~Ba)& Be ); Bi = rol32((Asa0^Da0), 1); Bo = rol32((Ake0^De0), 22); Bu = rol32((Abi0^Di0), 30); Ba = rol32((Amo0^Do1), 14); Be = rol32((Agu1^Du1), 10); Asa0 = Ba ^((~Be)& Bi ); Ake0 = Be ^((~Bi)& Bo ); Abi0 = Bi ^((~Bo)& Bu ); Amo0 = Bo ^((~Bu)& Ba ); Agu1 = Bu ^((~Ba)& Be ); Bu = rol32((Ama1^Da0), 9); Ba = rol32((Age1^De1), 1); Be = rol32((Asi1^Di0), 3); Bi = rol32((Ako0^Do1), 13); Bo = rol32((Abu1^Du0), 4); Ama1 = Ba ^((~Be)& Bi ); Age1 = Be ^((~Bi)& Bo ); Asi1 = Bi ^((~Bo)& Bu ); Ako0 = Bo ^((~Bu)& Ba ); Abu1 = Bu ^((~Ba)& Be ); Bu = rol32((Ama0^Da1), 9); Ba = (Age0^De0); Be = rol32((Asi0^Di1), 3); Bi = rol32((Ako1^Do0), 12); Bo = rol32((Abu0^Du1), 4); Ama0 = Ba ^((~Be)& Bi ); Age0 = Be ^((~Bi)& Bo ); Asi0 = Bi ^((~Bo)& Bu ); Ako1 = Bo ^((~Bu)& Ba ); Abu0 = Bu ^((~Ba)& Be ); Be = rol32((Aka1^Da0), 18); Bi = rol32((Abe1^De0), 5); Bo = rol32((Ami0^Di1), 8); Bu = rol32((Ago1^Do0), 28); Ba = rol32((Asu1^Du1), 14); Aka1 = Ba ^((~Be)& Bi ); Abe1 = Be ^((~Bi)& Bo ); Ami0 = Bi ^((~Bo)& Bu ); Ago1 = Bo ^((~Bu)& Ba ); Asu1 = Bu ^((~Ba)& Be ); Be = rol32((Aka0^Da1), 18); Bi = rol32((Abe0^De1), 5); Bo = rol32((Ami1^Di0), 7); Bu = rol32((Ago0^Do1), 28); Ba = rol32((Asu0^Du0), 13); Aka0 = Ba ^((~Be)& Bi ); Abe0 = Be ^((~Bi)& Bo ); Ami1 = Bi ^((~Bo)& Bu ); Ago0 = Bo ^((~Bu)& Ba ); Asu0 = Bu ^((~Ba)& Be ); Bo = rol32((Aga1^Da1), 21); Bu = rol32((Ase0^De0), 1); Ba = rol32((Aki1^Di0), 31); Be = rol32((Abo1^Do1), 28); Bi = rol32((Amu1^Du1), 20); Aga1 = Ba ^((~Be)& Bi ); Ase0 = Be ^((~Bi)& Bo ); Aki1 = Bi ^((~Bo)& Bu ); Abo1 = Bo ^((~Bu)& Ba ); Amu1 = Bu ^((~Ba)& Be ); Bo = rol32((Aga0^Da0), 20); Bu = rol32((Ase1^De1), 1); Ba = rol32((Aki0^Di1), 31); Be = rol32((Abo0^Do0), 27); Bi = rol32((Amu0^Du0), 19); Aga0 = Ba ^((~Be)& Bi ); Ase1 = Be ^((~Bi)& Bo ); Aki0 = Bi ^((~Bo)& Bu ); Abo0 = Bo ^((~Bu)& Ba ); Amu0 = Bu ^((~Ba)& Be ); KeccakAtoD_round2(); Ba = (Aba0^Da0); Be = rol32((Ake1^De0), 22); Bi = rol32((Asi0^Di1), 22); Bo = rol32((Ago0^Do1), 11); Bu = rol32((Amu1^Du0), 7); Aba0 = Ba ^((~Be)& Bi ); Aba0 ^= RoundConstants32[i+4]; Ake1 = Be ^((~Bi)& Bo ); Asi0 = Bi ^((~Bo)& Bu ); Ago0 = Bo ^((~Bu)& Ba ); Amu1 = Bu ^((~Ba)& Be ); Ba = (Aba1^Da1); Be = rol32((Ake0^De1), 22); Bi = rol32((Asi1^Di0), 21); Bo = rol32((Ago1^Do0), 10); Bu = rol32((Amu0^Du1), 7); Aba1 = Ba ^((~Be)& Bi ); Aba1 ^= RoundConstants32[i+5]; Ake0 = Be ^((~Bi)& Bo ); Asi1 = Bi ^((~Bo)& Bu ); Ago1 = Bo ^((~Bu)& Ba ); Amu0 = Bu ^((~Ba)& Be ); Bi = rol32((Ama0^Da1), 2); Bo = rol32((Abe0^De1), 23); Bu = rol32((Aki0^Di1), 31); Ba = rol32((Aso1^Do0), 14); Be = rol32((Agu0^Du0), 10); Ama0 = Ba ^((~Be)& Bi ); Abe0 = Be ^((~Bi)& Bo ); Aki0 = Bi ^((~Bo)& Bu ); Aso1 = Bo ^((~Bu)& Ba ); Agu0 = Bu ^((~Ba)& Be ); Bi = rol32((Ama1^Da0), 1); Bo = rol32((Abe1^De0), 22); Bu = rol32((Aki1^Di0), 30); Ba = rol32((Aso0^Do1), 14); Be = rol32((Agu1^Du1), 10); Ama1 = Ba ^((~Be)& Bi ); Abe1 = Be ^((~Bi)& Bo ); Aki1 = Bi ^((~Bo)& Bu ); Aso0 = Bo ^((~Bu)& Ba ); Agu1 = Bu ^((~Ba)& Be ); Bu = rol32((Aga1^Da0), 9); Ba = rol32((Ame0^De1), 1); Be = rol32((Abi1^Di0), 3); Bi = rol32((Ako1^Do1), 13); Bo = rol32((Asu1^Du0), 4); Aga1 = Ba ^((~Be)& Bi ); Ame0 = Be ^((~Bi)& Bo ); Abi1 = Bi ^((~Bo)& Bu ); Ako1 = Bo ^((~Bu)& Ba ); Asu1 = Bu ^((~Ba)& Be ); Bu = rol32((Aga0^Da1), 9); Ba = (Ame1^De0); Be = rol32((Abi0^Di1), 3); Bi = rol32((Ako0^Do0), 12); Bo = rol32((Asu0^Du1), 4); Aga0 = Ba ^((~Be)& Bi ); Ame1 = Be ^((~Bi)& Bo ); Abi0 = Bi ^((~Bo)& Bu ); Ako0 = Bo ^((~Bu)& Ba ); Asu0 = Bu ^((~Ba)& Be ); Be = rol32((Asa1^Da0), 18); Bi = rol32((Age1^De0), 5); Bo = rol32((Ami1^Di1), 8); Bu = rol32((Abo1^Do0), 28); Ba = rol32((Aku0^Du1), 14); Asa1 = Ba ^((~Be)& Bi ); Age1 = Be ^((~Bi)& Bo ); Ami1 = Bi ^((~Bo)& Bu ); Abo1 = Bo ^((~Bu)& Ba ); Aku0 = Bu ^((~Ba)& Be ); Be = rol32((Asa0^Da1), 18); Bi = rol32((Age0^De1), 5); Bo = rol32((Ami0^Di0), 7); Bu = rol32((Abo0^Do1), 28); Ba = rol32((Aku1^Du0), 13); Asa0 = Ba ^((~Be)& Bi ); Age0 = Be ^((~Bi)& Bo ); Ami0 = Bi ^((~Bo)& Bu ); Abo0 = Bo ^((~Bu)& Ba ); Aku1 = Bu ^((~Ba)& Be ); Bo = rol32((Aka0^Da1), 21); Bu = rol32((Ase0^De0), 1); Ba = rol32((Agi1^Di0), 31); Be = rol32((Amo0^Do1), 28); Bi = rol32((Abu0^Du1), 20); Aka0 = Ba ^((~Be)& Bi ); Ase0 = Be ^((~Bi)& Bo ); Agi1 = Bi ^((~Bo)& Bu ); Amo0 = Bo ^((~Bu)& Ba ); Abu0 = Bu ^((~Ba)& Be ); Bo = rol32((Aka1^Da0), 20); Bu = rol32((Ase1^De1), 1); Ba = rol32((Agi0^Di1), 31); Be = rol32((Amo1^Do0), 27); Bi = rol32((Abu1^Du0), 19); Aka1 = Ba ^((~Be)& Bi ); Ase1 = Be ^((~Bi)& Bo ); Agi0 = Bi ^((~Bo)& Bu ); Amo1 = Bo ^((~Bu)& Ba ); Abu1 = Bu ^((~Ba)& Be ); KeccakAtoD_round3(); Ba = (Aba0^Da0); Be = rol32((Abe0^De0), 22); Bi = rol32((Abi0^Di1), 22); Bo = rol32((Abo0^Do1), 11); Bu = rol32((Abu0^Du0), 7); Aba0 = Ba ^((~Be)& Bi ); Aba0 ^= RoundConstants32[i+6]; Abe0 = Be ^((~Bi)& Bo ); Abi0 = Bi ^((~Bo)& Bu ); Abo0 = Bo ^((~Bu)& Ba ); Abu0 = Bu ^((~Ba)& Be ); Ba = (Aba1^Da1); Be = rol32((Abe1^De1), 22); Bi = rol32((Abi1^Di0), 21); Bo = rol32((Abo1^Do0), 10); Bu = rol32((Abu1^Du1), 7); Aba1 = Ba ^((~Be)& Bi ); Aba1 ^= RoundConstants32[i+7]; Abe1 = Be ^((~Bi)& Bo ); Abi1 = Bi ^((~Bo)& Bu ); Abo1 = Bo ^((~Bu)& Ba ); Abu1 = Bu ^((~Ba)& Be ); Bi = rol32((Aga0^Da1), 2); Bo = rol32((Age0^De1), 23); Bu = rol32((Agi0^Di1), 31); Ba = rol32((Ago0^Do0), 14); Be = rol32((Agu0^Du0), 10); Aga0 = Ba ^((~Be)& Bi ); Age0 = Be ^((~Bi)& Bo ); Agi0 = Bi ^((~Bo)& Bu ); Ago0 = Bo ^((~Bu)& Ba ); Agu0 = Bu ^((~Ba)& Be ); Bi = rol32((Aga1^Da0), 1); Bo = rol32((Age1^De0), 22); Bu = rol32((Agi1^Di0), 30); Ba = rol32((Ago1^Do1), 14); Be = rol32((Agu1^Du1), 10); Aga1 = Ba ^((~Be)& Bi ); Age1 = Be ^((~Bi)& Bo ); Agi1 = Bi ^((~Bo)& Bu ); Ago1 = Bo ^((~Bu)& Ba ); Agu1 = Bu ^((~Ba)& Be ); Bu = rol32((Aka0^Da0), 9); Ba = rol32((Ake0^De1), 1); Be = rol32((Aki0^Di0), 3); Bi = rol32((Ako0^Do1), 13); Bo = rol32((Aku0^Du0), 4); Aka0 = Ba ^((~Be)& Bi ); Ake0 = Be ^((~Bi)& Bo ); Aki0 = Bi ^((~Bo)& Bu ); Ako0 = Bo ^((~Bu)& Ba ); Aku0 = Bu ^((~Ba)& Be ); Bu = rol32((Aka1^Da1), 9); Ba = (Ake1^De0); Be = rol32((Aki1^Di1), 3); Bi = rol32((Ako1^Do0), 12); Bo = rol32((Aku1^Du1), 4); Aka1 = Ba ^((~Be)& Bi ); Ake1 = Be ^((~Bi)& Bo ); Aki1 = Bi ^((~Bo)& Bu ); Ako1 = Bo ^((~Bu)& Ba ); Aku1 = Bu ^((~Ba)& Be ); Be = rol32((Ama0^Da0), 18); Bi = rol32((Ame0^De0), 5); Bo = rol32((Ami0^Di1), 8); Bu = rol32((Amo0^Do0), 28); Ba = rol32((Amu0^Du1), 14); Ama0 = Ba ^((~Be)& Bi ); Ame0 = Be ^((~Bi)& Bo ); Ami0 = Bi ^((~Bo)& Bu ); Amo0 = Bo ^((~Bu)& Ba ); Amu0 = Bu ^((~Ba)& Be ); Be = rol32((Ama1^Da1), 18); Bi = rol32((Ame1^De1), 5); Bo = rol32((Ami1^Di0), 7); Bu = rol32((Amo1^Do1), 28); Ba = rol32((Amu1^Du0), 13); Ama1 = Ba ^((~Be)& Bi ); Ame1 = Be ^((~Bi)& Bo ); Ami1 = Bi ^((~Bo)& Bu ); Amo1 = Bo ^((~Bu)& Ba ); Amu1 = Bu ^((~Ba)& Be ); Bo = rol32((Asa0^Da1), 21); Bu = rol32((Ase0^De0), 1); Ba = rol32((Asi0^Di0), 31); Be = rol32((Aso0^Do1), 28); Bi = rol32((Asu0^Du1), 20); Asa0 = Ba ^((~Be)& Bi ); Ase0 = Be ^((~Bi)& Bo ); Asi0 = Bi ^((~Bo)& Bu ); Aso0 = Bo ^((~Bu)& Ba ); Asu0 = Bu ^((~Ba)& Be ); Bo = rol32((Asa1^Da0), 20); Bu = rol32((Ase1^De1), 1); Ba = rol32((Asi1^Di1), 31); Be = rol32((Aso1^Do0), 27); Bi = rol32((Asu1^Du0), 19); Asa1 = Ba ^((~Be)& Bi ); Ase1 = Be ^((~Bi)& Bo ); Asi1 = Bi ^((~Bo)& Bu ); Aso1 = Bo ^((~Bu)& Ba ); Asu1 = Bu ^((~Ba)& Be ); } } static void xor_lane(struct KeccakContext *ctx, int lane, uint64_t val) { uint32_t x0, x1, t; uint32_t *dst = ctx->u.state32 + lane*2; x0 = val; t = (x0 ^ (x0 >> 1)) & 0x22222222; x0 = x0 ^ t ^ (t << 1); t = (x0 ^ (x0 >> 2)) & 0x0C0C0C0C; x0 = x0 ^ t ^ (t << 2); t = (x0 ^ (x0 >> 4)) & 0x00F000F0; x0 = x0 ^ t ^ (t << 4); t = (x0 ^ (x0 >> 8)) & 0x0000FF00; x0 = x0 ^ t ^ (t << 8); x1 = val >> 32; t = (x1 ^ (x1 >> 1)) & 0x22222222; x1 = x1 ^ t ^ (t << 1); t = (x1 ^ (x1 >> 2)) & 0x0C0C0C0C; x1 = x1 ^ t ^ (t << 2); t = (x1 ^ (x1 >> 4)) & 0x00F000F0; x1 = x1 ^ t ^ (t << 4); t = (x1 ^ (x1 >> 8)) & 0x0000FF00; x1 = x1 ^ t ^ (t << 8); dst[0] ^= (x0 & 0x0000FFFF) | (x1 << 16); dst[1] ^= (x0 >> 16) | (x1 & 0xFFFF0000); } static void extract(uint8_t *dst, const struct KeccakContext *ctx, int startLane, int laneCount) { const uint32_t *src = ctx->u.state32 + startLane * 2; uint32_t t, x0, x1; while (laneCount--) { x0 = *src++; x1 = *src++; t = (x0 & 0x0000FFFF) | (x1 << 16); x1 = (x0 >> 16) | (x1 & 0xFFFF0000); x0 = t; t = (x0 ^ (x0 >> 8)) & 0x0000FF00; x0 = x0 ^ t ^ (t << 8); t = (x0 ^ (x0 >> 4)) & 0x00F000F0; x0 = x0 ^ t ^ (t << 4); t = (x0 ^ (x0 >> 2)) & 0x0C0C0C0C; x0 = x0 ^ t ^ (t << 2); t = (x0 ^ (x0 >> 1)) & 0x22222222; x0 = x0 ^ t ^ (t << 1); t = (x1 ^ (x1 >> 8)) & 0x0000FF00; x1 = x1 ^ t ^ (t << 8); t = (x1 ^ (x1 >> 4)) & 0x00F000F0; x1 = x1 ^ t ^ (t << 4); t = (x1 ^ (x1 >> 2)) & 0x0C0C0C0C; x1 = x1 ^ t ^ (t << 2); t = (x1 ^ (x1 >> 1)) & 0x22222222; x1 = x1 ^ t ^ (t << 1); le32enc(dst + 0, x0); le32enc(dst + 4, x1); dst += 8; } } #endif /* KECCAK_32BIT */ /* * Common code */ static void xor_byte(struct KeccakContext *ctx, int nbyte, uint8_t val) { int o = nbyte / 8; int s = (nbyte % 8) * 8; xor_lane(ctx, o, (uint64_t)(val) << s); } static void add_bytes(struct KeccakContext *ctx, const uint8_t *p, unsigned int ofs, unsigned int len) { uint64_t w; unsigned int m = ofs % 8; /* partial word */ if (m) { m = 8 - m; if (m > len) m = len; while (m--) { xor_byte(ctx, ofs++, *p++); len--; } } /* full words */ while (len >= 8) { w = le64dec(p); xor_lane(ctx, ofs / 8, w); ofs += 8; p += 8; len -= 8; } /* partial word */ while (len--) xor_byte(ctx, ofs++, *p++); } static void extract_bytes(struct KeccakContext *ctx, uint8_t *dst, unsigned int ofs, unsigned int count) { uint8_t lanebuf[8]; unsigned int n, avail; if (ofs % 8 != 0 || count < 8) { avail = 8 - ofs % 8; n = (avail > count) ? count : avail; extract(lanebuf, ctx, ofs/8, 1); memcpy(dst, lanebuf + ofs%8, n); dst += n; ofs += n; count -= n; } if (count > 8) { n = count / 8; extract(dst, ctx, ofs/8, n); dst += n*8; ofs += n*8; count -= n*8; } if (count > 0) { extract(lanebuf, ctx, ofs/8, 1); memcpy(dst, lanebuf, count); } memset(lanebuf, 0, sizeof(lanebuf)); } static inline void permute_if_needed(struct KeccakContext *ctx) { if (ctx->pos == ctx->rbytes) { keccak_f(ctx); ctx->pos = 0; } } /* * Public API */ int keccak_init(struct KeccakContext *ctx, unsigned int capacity) { if (capacity % 8 != 0 || capacity < 8 || capacity > (1600 - 8)) return 0; memset(ctx, 0, sizeof(struct KeccakContext)); ctx->rbytes = (1600 - capacity) / 8; return 1; } void keccak_absorb(struct KeccakContext *ctx, const void *data, size_t len) { unsigned int n, avail; const uint8_t *src = data; while (len > 0) { avail = ctx->rbytes - ctx->pos; n = (len > avail) ? avail : len; add_bytes(ctx, src, ctx->pos, n); src += n; len -= n; ctx->pos += n; permute_if_needed(ctx); } } void keccak_squeeze(struct KeccakContext *ctx, uint8_t *dst, size_t len) { unsigned int avail, n; while (len > 0) { avail = ctx->rbytes - ctx->pos; n = (len > avail) ? avail : len; extract_bytes(ctx, dst, ctx->pos, n); ctx->pos += n; dst += n; len -= n; permute_if_needed(ctx); } } void keccak_squeeze_xor(struct KeccakContext *ctx, uint8_t *dst, const void *data, size_t len) { const uint8_t *src = data; unsigned int n, avail, i; while (len > 0) { avail = ctx->rbytes - ctx->pos; n = (len > avail) ? avail : len; extract_bytes(ctx, dst, ctx->pos, n); for (i = 0; i < n; i++) dst[i] ^= src[i]; ctx->pos += n; src += n; dst += n; len -= n; permute_if_needed(ctx); } } void keccak_encrypt(struct KeccakContext *ctx, uint8_t *dst, const void *data, size_t len) { const uint8_t *src = data; unsigned int n, avail; while (len > 0) { avail = ctx->rbytes - ctx->pos; n = (len > avail) ? avail : len; add_bytes(ctx, src, ctx->pos, n); extract_bytes(ctx, dst, ctx->pos, n); ctx->pos += n; src += n; dst += n; len -= n; permute_if_needed(ctx); } } void keccak_decrypt(struct KeccakContext *ctx, uint8_t *dst, const void *data, size_t len) { const uint8_t *src = data; unsigned int n, avail, i; while (len > 0) { avail = ctx->rbytes - ctx->pos; n = (len > avail) ? avail : len; extract_bytes(ctx, dst, ctx->pos, n); for (i = 0; i < n; i++) dst[i] ^= src[i]; add_bytes(ctx, dst, ctx->pos, n); ctx->pos += n; src += n; dst += n; len -= n; permute_if_needed(ctx); } } void keccak_pad(struct KeccakContext *ctx, const void *pad, size_t len) { const uint8_t *src = pad; if (len > 0) { if (len > 1) { keccak_absorb(ctx, src, len - 1); src += len - 1; } xor_byte(ctx, ctx->pos, src[0]); xor_byte(ctx, ctx->rbytes - 1, 0x80); } keccak_f(ctx); ctx->pos = 0; } void keccak_rewind(struct KeccakContext *ctx) { ctx->pos = 0; } void keccak_forget(struct KeccakContext *ctx) { unsigned int rem = ctx->rbytes % 8; uint8_t buf[8]; memset(ctx->u.state32, 0, ctx->rbytes - rem); if (rem) { extract_bytes(ctx, buf, ctx->rbytes - rem, rem); add_bytes(ctx, buf, ctx->rbytes - rem, rem); memset(buf, 0, sizeof(buf)); } ctx->pos = 0; } pgbouncer-1.7/lib/usual/crypto/sha3.h0000664000175000017500000000711012556647420014530 00000000000000/* * SHA3 implementation. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * SHA3 variants of Keccak. * * SHA3-X are fixed-length hashes, SHAKE is variable-length. */ #ifndef _USUAL_CRYPTO_SHA3_H_ #define _USUAL_CRYPTO_SHA3_H_ #include /** Keccak capacity area for SHA3-224, in bits */ #define SHA3_224_CAPACITY 448 /** Keccak capacity area for SHA3-256, in bits */ #define SHA3_256_CAPACITY 512 /** Keccak capacity area for SHA3-384, in bits */ #define SHA3_384_CAPACITY 768 /** Keccak capacity area for SHA3-512, in bits */ #define SHA3_512_CAPACITY 1024 /** Keccak capacity area for SHAKE128, in bits */ #define SHAKE128_CAPACITY 256 /** Keccak capacity area for SHAKE256, in bits */ #define SHAKE256_CAPACITY 512 /** Result length of SHA3-224, in bytes */ #define SHA3_224_DIGEST_LENGTH (224/8) /** Result length of SHA3-256, in bytes */ #define SHA3_256_DIGEST_LENGTH (256/8) /** Result length of SHA3-384, in bytes */ #define SHA3_384_DIGEST_LENGTH (384/8) /** Result length of SHA3-512, in bytes */ #define SHA3_512_DIGEST_LENGTH (512/8) /** Result length of SHAKE128, in bytes */ #define SHAKE128_DIGEST_LENGTH (256/8) /** Result length of SHAKE256, in bytes */ #define SHAKE256_DIGEST_LENGTH (512/8) /** Block size of SHA3-224, in bytes */ #define SHA3_224_BLOCK_SIZE ((1600 - SHA3_224_CAPACITY) / 8) /** Block size of SHA3-256, in bytes */ #define SHA3_256_BLOCK_SIZE ((1600 - SHA3_256_CAPACITY) / 8) /** Block size of SHA3-384, in bytes */ #define SHA3_384_BLOCK_SIZE ((1600 - SHA3_384_CAPACITY) / 8) /** Block size of SHA3-512, in bytes */ #define SHA3_512_BLOCK_SIZE ((1600 - SHA3_512_CAPACITY) / 8) /** Block size of SHAKE128, in bytes */ #define SHAKE128_BLOCK_SIZE ((1600 - SHAKE128_CAPACITY) / 8) /** Block size of SHAKE256, in bytes */ #define SHAKE256_BLOCK_SIZE ((1600 - SHAKE256_CAPACITY) / 8) /** * State structure. */ struct SHA3Context { struct KeccakContext kctx; bool padded; uint8_t pad; unsigned int obytes; }; /** Initialize state for SHA3-224 */ void sha3_224_reset(struct SHA3Context *ctx); /** Initialize state for SHA3-256 */ void sha3_256_reset(struct SHA3Context *ctx); /** Initialize state for SHA3-384 */ void sha3_384_reset(struct SHA3Context *ctx); /** Initialize state for SHA3-512 */ void sha3_512_reset(struct SHA3Context *ctx); /** Process data, update state */ void sha3_update(struct SHA3Context *ctx, const void *ptr, unsigned len); /** Calculate final result */ void sha3_final(struct SHA3Context *ctx, void *dst); /** Initialize state for SHAKE128 */ void shake128_reset(struct SHA3Context *ctx); /** Initialize state for SHAKE256 */ void shake256_reset(struct SHA3Context *ctx); /** Process data, update state */ void shake_update(struct SHA3Context *ctx, const void *ptr, unsigned len); /** Output variable amount of result data */ void shake_extract(struct SHA3Context *ctx, void *dst, unsigned count); #endif pgbouncer-1.7/lib/usual/crypto/sha1.c0000664000175000017500000000740112511203511014500 00000000000000/* * SHA1 implementation based on RFC3174. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #define bufpos(ctx) ((ctx)->nbytes & (SHA1_BLOCK_SIZE - 1)) /* * SHA1 core. */ #define W(n) (buf[(n) & 15]) #define setW(n, val) W(n) = val /* base SHA1 operation */ #define SHA1OP(_t, fn, K) do { \ uint32_t tmp, t = (_t); \ if (t >= 16) { \ tmp = W(t - 3) ^ W(t - 8) ^ W(t - 14) ^ W(t - 16); \ setW(t, rol32(tmp, 1)); \ } else { \ /* convert endianess on first go */ \ setW(t, be32toh(W(t))); \ } \ tmp = rol32(a, 5) + fn(b, c, d) + e + W(t) + K; \ e = d; d = c; c = rol32(b, 30); b = a; a = tmp; \ } while (0) /* mix functions */ #define F0(b, c, d) (d ^ (b & (c ^ d))) #define F1(b, c, d) (b ^ c ^ d) #define F2(b, c, d) ((b & c) | (b & d) | (c & d)) #define F3(b, c, d) (b ^ c ^ d) /* operation details for each round */ #define SHA1R0(t) SHA1OP(t, F0, 0x5a827999) #define SHA1R1(t) SHA1OP(t, F1, 0x6ed9eba1) #define SHA1R2(t) SHA1OP(t, F2, 0x8f1bbcdc) #define SHA1R3(t) SHA1OP(t, F3, 0xca62c1d6) /* repeat with increasing offset */ #define R4(R, t) R(t+0); R(t+1); R(t+2); R(t+3) #define R16(R, t) R4(R, t+0); R4(R, t+4); R4(R, t+8); R4(R, t+12) #define R20(R, t) R16(R, t+0); R4(R, t+16) static void sha1_core(struct sha1_ctx * ctx, uint32_t *buf) { uint32_t a, b, c, d, e; a = ctx->a; b = ctx->b; c = ctx->c; d = ctx->d; e = ctx->e; R20(SHA1R0, 0); R20(SHA1R1, 20); R20(SHA1R2, 40); R20(SHA1R3, 60); ctx->a += a; ctx->b += b; ctx->c += c; ctx->d += d; ctx->e += e; } /* * Public API. */ void sha1_reset(struct sha1_ctx *ctx) { ctx->nbytes = 0; ctx->a = 0x67452301; ctx->b = 0xefcdab89; ctx->c = 0x98badcfe; ctx->d = 0x10325476; ctx->e = 0xc3d2e1f0; } void sha1_update(struct sha1_ctx *ctx, const void *data, unsigned int len) { unsigned int n; const uint8_t *src = data; uint8_t *dst = (uint8_t *)ctx->buf; while (len > 0) { n = SHA1_BLOCK_SIZE - bufpos(ctx); if (n > len) n = len; memcpy(dst + bufpos(ctx), src, n); src += n; len -= n; ctx->nbytes += n; if (bufpos(ctx) == 0) sha1_core(ctx, ctx->buf); } } void sha1_final(struct sha1_ctx *ctx, uint8_t *dst) { static const uint8_t padding[SHA1_BLOCK_SIZE] = { 0x80 }; uint64_t nbits = ctx->nbytes * 8; int pad_len, pos = bufpos(ctx); /* add padding */ pad_len = SHA1_BLOCK_SIZE - 8 - pos; if (pad_len <= 0) pad_len += SHA1_BLOCK_SIZE; sha1_update(ctx, padding, pad_len); /* add length */ ctx->buf[14] = htobe32(nbits >> 32); ctx->buf[15] = htobe32(nbits); /* final result */ sha1_core(ctx, ctx->buf); be32enc(dst + 0*4, ctx->a); be32enc(dst + 1*4, ctx->b); be32enc(dst + 2*4, ctx->c); be32enc(dst + 3*4, ctx->d); be32enc(dst + 4*4, ctx->e); } /* * DigestInfo */ static const struct DigestInfo sha1_info = { (DigestInitFunc *)sha1_reset, (DigestUpdateFunc *)sha1_update, (DigestFinalFunc *)sha1_final, sizeof(struct sha1_ctx), SHA1_DIGEST_LENGTH, SHA1_BLOCK_SIZE }; const struct DigestInfo *digest_SHA1(void) { return &sha1_info; } pgbouncer-1.7/lib/usual/crypto/digest.c0000664000175000017500000000365312511203511015130 00000000000000/* * Common API for cryptographic digests. * * Copyright (c) 2012 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include struct DigestContext { const struct DigestInfo *impl; CxMem *cx; uint64_t state[1]; }; struct DigestContext *digest_new(const struct DigestInfo *impl, CxMem *cx) { struct DigestContext *ctx; unsigned alloc; alloc = offsetof(struct DigestContext, state) + impl->state_len; ctx = cx_alloc(cx, alloc); if (!ctx) return NULL; ctx->impl = impl; ctx->cx = cx; impl->init(ctx->state); return ctx; } void digest_update(struct DigestContext *ctx, const void *data, size_t len) { ctx->impl->update(ctx->state, data, len); } void digest_final(struct DigestContext *ctx, uint8_t *res) { ctx->impl->final(ctx->state, res); } void digest_reset(struct DigestContext *ctx) { ctx->impl->init(ctx->state); } void digest_free(struct DigestContext *ctx) { CxMem *cx = ctx->cx; unsigned alloc = offsetof(struct DigestContext, state) + ctx->impl->state_len; memset(ctx, 0, alloc); cx_free(cx, ctx); } unsigned digest_block_len(struct DigestContext *ctx) { return ctx->impl->block_len; } unsigned digest_result_len(struct DigestContext *ctx) { return ctx->impl->result_len; } pgbouncer-1.7/lib/usual/crypto/entropy.h0000664000175000017500000000222412567562476015404 00000000000000/* * Load entropy from kernel. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * Load entropy from OS. */ #ifndef _USUAL_CRYPTO_ENTROPY_H_ #define _USUAL_CRYPTO_ENTROPY_H_ #include #ifndef HAVE_GETENTROPY #define getentropy(dst, len) usual_getentropy(dst, len) /** * Fetch entropy from OS kernel. */ int getentropy(void *dst, size_t len); #endif /* !HAVE_GETENTROPY */ #endif /* _USUAL_CRYPTO_ENTROPY_H_ */ pgbouncer-1.7/lib/usual/crypto/sha3.c0000664000175000017500000001051712511203511014504 00000000000000/* * SHA3 implementation. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #define PAD_SHA3 0x06 #define PAD_SHAKE 0x1f void sha3_224_reset(struct SHA3Context *ctx) { keccak_init(&ctx->kctx, SHA3_224_CAPACITY); ctx->padded = 0; ctx->obytes = SHA3_224_DIGEST_LENGTH; ctx->pad = PAD_SHA3; } void sha3_256_reset(struct SHA3Context *ctx) { keccak_init(&ctx->kctx, SHA3_256_CAPACITY); ctx->padded = 0; ctx->obytes = SHA3_256_DIGEST_LENGTH; ctx->pad = PAD_SHA3; } void sha3_384_reset(struct SHA3Context *ctx) { keccak_init(&ctx->kctx, SHA3_384_CAPACITY); ctx->padded = 0; ctx->obytes = SHA3_384_DIGEST_LENGTH; ctx->pad = PAD_SHA3; } void sha3_512_reset(struct SHA3Context *ctx) { keccak_init(&ctx->kctx, SHA3_512_CAPACITY); ctx->padded = 0; ctx->obytes = SHA3_512_DIGEST_LENGTH; ctx->pad = PAD_SHA3; } void shake128_reset(struct SHA3Context *ctx) { keccak_init(&ctx->kctx, SHAKE128_CAPACITY); ctx->padded = 0; ctx->obytes = SHAKE128_DIGEST_LENGTH; ctx->pad = PAD_SHAKE; } void shake256_reset(struct SHA3Context *ctx) { keccak_init(&ctx->kctx, SHAKE256_CAPACITY); ctx->padded = 0; ctx->obytes = SHAKE256_DIGEST_LENGTH; ctx->pad = PAD_SHAKE; } void sha3_update(struct SHA3Context *ctx, const void *ptr, unsigned len) { keccak_absorb(&ctx->kctx, ptr, len); } void sha3_final(struct SHA3Context *ctx, void *dst) { if (!ctx->padded) { keccak_pad(&ctx->kctx, &ctx->pad, 1); ctx->padded = 1; } keccak_squeeze(&ctx->kctx, dst, ctx->obytes); } void shake_update(struct SHA3Context *ctx, const void *ptr, unsigned len) { keccak_absorb(&ctx->kctx, ptr, len); } void shake_extract(struct SHA3Context *ctx, void *dst, unsigned count) { if (!ctx->padded) { keccak_pad(&ctx->kctx, &ctx->pad, 1); ctx->padded = 1; } keccak_squeeze(&ctx->kctx, dst, count); } /* * DigestInfo */ static const struct DigestInfo sha3_224_info = { (DigestInitFunc *)sha3_224_reset, (DigestUpdateFunc *)sha3_update, (DigestFinalFunc *)sha3_final, sizeof(struct SHA3Context), SHA3_224_DIGEST_LENGTH, SHA3_224_BLOCK_SIZE }; static const struct DigestInfo sha3_256_info = { (DigestInitFunc *)sha3_256_reset, (DigestUpdateFunc *)sha3_update, (DigestFinalFunc *)sha3_final, sizeof(struct SHA3Context), SHA3_256_DIGEST_LENGTH, SHA3_256_BLOCK_SIZE }; static const struct DigestInfo sha3_384_info = { (DigestInitFunc *)sha3_384_reset, (DigestUpdateFunc *)sha3_update, (DigestFinalFunc *)sha3_final, sizeof(struct SHA3Context), SHA3_384_DIGEST_LENGTH, SHA3_384_BLOCK_SIZE }; static const struct DigestInfo sha3_512_info = { (DigestInitFunc *)sha3_512_reset, (DigestUpdateFunc *)sha3_update, (DigestFinalFunc *)sha3_final, sizeof(struct SHA3Context), SHA3_512_DIGEST_LENGTH, SHA3_512_BLOCK_SIZE }; static const struct DigestInfo shake128_info = { (DigestInitFunc *)shake128_reset, (DigestUpdateFunc *)sha3_update, (DigestFinalFunc *)sha3_final, sizeof(struct SHA3Context), SHAKE128_DIGEST_LENGTH, SHAKE128_BLOCK_SIZE }; static const struct DigestInfo shake256_info = { (DigestInitFunc *)shake256_reset, (DigestUpdateFunc *)sha3_update, (DigestFinalFunc *)sha3_final, sizeof(struct SHA3Context), SHAKE256_DIGEST_LENGTH, SHAKE256_BLOCK_SIZE }; const struct DigestInfo *digest_SHA3_224(void) { return &sha3_224_info; } const struct DigestInfo *digest_SHA3_256(void) { return &sha3_256_info; } const struct DigestInfo *digest_SHA3_384(void) { return &sha3_384_info; } const struct DigestInfo *digest_SHA3_512(void) { return &sha3_512_info; } const struct DigestInfo *digest_SHAKE128(void) { return &shake128_info; } const struct DigestInfo *digest_SHAKE256(void) { return &shake256_info; } pgbouncer-1.7/lib/usual/crypto/chacha.c0000664000175000017500000001051312511203511015051 00000000000000/* * ChaCha cipher. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Based on: chacha-ref.c version 20080118 / D. J. Bernstein / Public domain. */ #include #include #include #define CHACHA_ROUNDS 20 #define QUARTERROUND(in, out, a, b, c, d) \ do { \ out[a] = in[a] + in[b]; out[d] = rol32(in[d] ^ out[a], 16); \ out[c] = in[c] + out[d]; out[b] = rol32(in[b] ^ out[c], 12); \ out[a] = out[a] + out[b]; out[d] = rol32(out[d] ^ out[a], 8); \ out[c] = out[c] + out[d]; out[b] = rol32(out[b] ^ out[c], 7); \ } while (0) #define OUTPUT(a,b,c,d) \ do { \ output[a] = htole32(x[a] + input[a]); \ output[b] = htole32(x[b] + input[b]); \ output[c] = htole32(x[c] + input[c]); \ output[d] = htole32(x[d] + input[d]); \ } while (0) /* mix full state. needs 2 call sites to avoid inlining */ static void chacha_mix(struct ChaCha *ctx) { const uint32_t *input = ctx->state; uint32_t *output = ctx->u.output32; int i; uint32_t x[16]; /* first "column" round */ QUARTERROUND(input, x, 0, 4, 8, 12); QUARTERROUND(input, x, 1, 5, 9, 13); QUARTERROUND(input, x, 2, 6, 10, 14); QUARTERROUND(input, x, 3, 7, 11, 15); for (i = 0; i < CHACHA_ROUNDS/2 - 1; i++) { /* "diagonal" round */ QUARTERROUND(x, x, 0, 5, 10, 15); QUARTERROUND(x, x, 1, 6, 11, 12); QUARTERROUND(x, x, 2, 7, 8, 13); QUARTERROUND(x, x, 3, 4, 9, 14); /* "column" round */ QUARTERROUND(x, x, 0, 4, 8, 12); QUARTERROUND(x, x, 1, 5, 9, 13); QUARTERROUND(x, x, 2, 6, 10, 14); QUARTERROUND(x, x, 3, 7, 11, 15); } /* last "diagonal" round */ QUARTERROUND(x, x, 0, 5, 10, 15); OUTPUT(0, 5, 10, 15); QUARTERROUND(x, x, 1, 6, 11, 12); OUTPUT(1, 6, 11, 12); QUARTERROUND(x, x, 2, 7, 8, 13); OUTPUT(2, 7, 8, 13); QUARTERROUND(x, x, 3, 4, 9, 14); OUTPUT(3, 4, 9, 14); ctx->pos = 0; ctx->state[12]++; if (!ctx->state[12]) ctx->state[13]++; } void chacha_set_key_256(struct ChaCha *ctx, const void *key) { unsigned int i; memcpy(&ctx->state[0], "expand 32-byte k", 16); memcpy(&ctx->state[4], key, 32); for (i = 0; i < 12; i++) ctx->state[i] = le32toh(ctx->state[i]); ctx->pos = CHACHA_BLOCK_SIZE; } void chacha_set_key_128(struct ChaCha *ctx, const void *key) { unsigned int i; memcpy(&ctx->state[0], "expand 16-byte k", 16); memcpy(&ctx->state[4], key, 16); memcpy(&ctx->state[8], key, 16); for (i = 0; i < 12; i++) ctx->state[i] = le32toh(ctx->state[i]); ctx->pos = CHACHA_BLOCK_SIZE; } void chacha_set_nonce(struct ChaCha *ctx, uint32_t counter_low, uint32_t counter_high, const void *iv) { const uint8_t *_iv = iv; ctx->state[12] = counter_low; ctx->state[13] = counter_high; if (_iv) { ctx->state[14] = le32dec(_iv); ctx->state[15] = le32dec(_iv + 4); } ctx->pos = CHACHA_BLOCK_SIZE; } void chacha_keystream(struct ChaCha *ctx, void *stream, size_t bytes) { unsigned int n, avail; const uint8_t *ks = ctx->u.output8; uint8_t *dst = stream; while (bytes > 0) { if (ctx->pos >= CHACHA_BLOCK_SIZE) chacha_mix(ctx); avail = CHACHA_BLOCK_SIZE - ctx->pos; n = (bytes > avail) ? avail : bytes; memcpy(dst, ks + ctx->pos, n); bytes -= n; dst += n; ctx->pos += n; } } void chacha_keystream_xor(struct ChaCha *ctx, const void *plain, void *encrypted, size_t bytes) { unsigned int i, n, avail; const uint8_t *ks = ctx->u.output8; const uint8_t *src = plain; uint8_t *dst = encrypted; while (bytes > 0) { if (ctx->pos >= CHACHA_BLOCK_SIZE) chacha_mix(ctx); avail = CHACHA_BLOCK_SIZE - ctx->pos; n = (bytes > avail) ? avail : bytes; for (i = 0; i < n; i++) dst[i] = src[i] ^ ks[i]; bytes -= n; dst += n; src += n; ctx->pos += n; } } pgbouncer-1.7/lib/usual/crypto/md5.c0000664000175000017500000001304612511203511014333 00000000000000/* * MD5 implementation based on RFC1321. * * Copyright (c) 2008 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include /* * Support functions. */ #define bufpos(ctx) ((ctx)->nbytes & (MD5_BLOCK_LENGTH - 1)) static inline void swap_words(uint32_t *w, int n) { #ifdef WORDS_BIGENDIAN for (; n > 0; w++, n--) *w = le32toh(*w); #endif } /* * MD5 core. */ #define F(X,Y,Z) ((X & Y) | ((~X) & Z)) #define G(X,Y,Z) ((X & Z) | (Y & (~Z))) #define H(X,Y,Z) (X ^ Y ^ Z) #define I(X,Y,Z) (Y ^ (X | (~Z))) #define OP(fn, a, b, c, d, k, s, T_i) \ a = b + rol32(a + fn(b, c, d) + X[k] + T_i, s) static void md5_mix(struct md5_ctx *ctx, const uint32_t *X) { uint32_t a, b, c, d; a = ctx->a; b = ctx->b; c = ctx->c; d = ctx->d; /* Round 1. */ OP(F, a, b, c, d, 0, 7, 0xd76aa478); OP(F, d, a, b, c, 1, 12, 0xe8c7b756); OP(F, c, d, a, b, 2, 17, 0x242070db); OP(F, b, c, d, a, 3, 22, 0xc1bdceee); OP(F, a, b, c, d, 4, 7, 0xf57c0faf); OP(F, d, a, b, c, 5, 12, 0x4787c62a); OP(F, c, d, a, b, 6, 17, 0xa8304613); OP(F, b, c, d, a, 7, 22, 0xfd469501); OP(F, a, b, c, d, 8, 7, 0x698098d8); OP(F, d, a, b, c, 9, 12, 0x8b44f7af); OP(F, c, d, a, b, 10, 17, 0xffff5bb1); OP(F, b, c, d, a, 11, 22, 0x895cd7be); OP(F, a, b, c, d, 12, 7, 0x6b901122); OP(F, d, a, b, c, 13, 12, 0xfd987193); OP(F, c, d, a, b, 14, 17, 0xa679438e); OP(F, b, c, d, a, 15, 22, 0x49b40821); /* Round 2. */ OP(G, a, b, c, d, 1, 5, 0xf61e2562); OP(G, d, a, b, c, 6, 9, 0xc040b340); OP(G, c, d, a, b, 11, 14, 0x265e5a51); OP(G, b, c, d, a, 0, 20, 0xe9b6c7aa); OP(G, a, b, c, d, 5, 5, 0xd62f105d); OP(G, d, a, b, c, 10, 9, 0x02441453); OP(G, c, d, a, b, 15, 14, 0xd8a1e681); OP(G, b, c, d, a, 4, 20, 0xe7d3fbc8); OP(G, a, b, c, d, 9, 5, 0x21e1cde6); OP(G, d, a, b, c, 14, 9, 0xc33707d6); OP(G, c, d, a, b, 3, 14, 0xf4d50d87); OP(G, b, c, d, a, 8, 20, 0x455a14ed); OP(G, a, b, c, d, 13, 5, 0xa9e3e905); OP(G, d, a, b, c, 2, 9, 0xfcefa3f8); OP(G, c, d, a, b, 7, 14, 0x676f02d9); OP(G, b, c, d, a, 12, 20, 0x8d2a4c8a); /* Round 3. */ OP(H, a, b, c, d, 5, 4, 0xfffa3942); OP(H, d, a, b, c, 8, 11, 0x8771f681); OP(H, c, d, a, b, 11, 16, 0x6d9d6122); OP(H, b, c, d, a, 14, 23, 0xfde5380c); OP(H, a, b, c, d, 1, 4, 0xa4beea44); OP(H, d, a, b, c, 4, 11, 0x4bdecfa9); OP(H, c, d, a, b, 7, 16, 0xf6bb4b60); OP(H, b, c, d, a, 10, 23, 0xbebfbc70); OP(H, a, b, c, d, 13, 4, 0x289b7ec6); OP(H, d, a, b, c, 0, 11, 0xeaa127fa); OP(H, c, d, a, b, 3, 16, 0xd4ef3085); OP(H, b, c, d, a, 6, 23, 0x04881d05); OP(H, a, b, c, d, 9, 4, 0xd9d4d039); OP(H, d, a, b, c, 12, 11, 0xe6db99e5); OP(H, c, d, a, b, 15, 16, 0x1fa27cf8); OP(H, b, c, d, a, 2, 23, 0xc4ac5665); /* Round 4. */ OP(I, a, b, c, d, 0, 6, 0xf4292244); OP(I, d, a, b, c, 7, 10, 0x432aff97); OP(I, c, d, a, b, 14, 15, 0xab9423a7); OP(I, b, c, d, a, 5, 21, 0xfc93a039); OP(I, a, b, c, d, 12, 6, 0x655b59c3); OP(I, d, a, b, c, 3, 10, 0x8f0ccc92); OP(I, c, d, a, b, 10, 15, 0xffeff47d); OP(I, b, c, d, a, 1, 21, 0x85845dd1); OP(I, a, b, c, d, 8, 6, 0x6fa87e4f); OP(I, d, a, b, c, 15, 10, 0xfe2ce6e0); OP(I, c, d, a, b, 6, 15, 0xa3014314); OP(I, b, c, d, a, 13, 21, 0x4e0811a1); OP(I, a, b, c, d, 4, 6, 0xf7537e82); OP(I, d, a, b, c, 11, 10, 0xbd3af235); OP(I, c, d, a, b, 2, 15, 0x2ad7d2bb); OP(I, b, c, d, a, 9, 21, 0xeb86d391); ctx->a += a; ctx->b += b; ctx->c += c; ctx->d += d; } /* * Public API. */ void md5_reset(struct md5_ctx *ctx) { ctx->nbytes = 0; ctx->a = 0x67452301; ctx->b = 0xefcdab89; ctx->c = 0x98badcfe; ctx->d = 0x10325476; } void md5_update(struct md5_ctx *ctx, const void *data, unsigned int len) { unsigned int n; const uint8_t *ptr = data; uint8_t *buf = (uint8_t *)ctx->buf; while (len > 0) { n = MD5_BLOCK_LENGTH - bufpos(ctx); if (n > len) n = len; memcpy(buf + bufpos(ctx), ptr, n); ptr += n; len -= n; ctx->nbytes += n; if (bufpos(ctx) == 0) { swap_words(ctx->buf, 16); md5_mix(ctx, ctx->buf); } } } void md5_final(struct md5_ctx *ctx, uint8_t *dst) { static const uint8_t padding[MD5_BLOCK_LENGTH] = { 0x80 }; uint64_t final_len = ctx->nbytes * 8; int pad_len, pos = bufpos(ctx); /* add padding */ pad_len = MD5_BLOCK_LENGTH - 8 - pos; if (pad_len <= 0) pad_len += MD5_BLOCK_LENGTH; md5_update(ctx, padding, pad_len); /* add length directly */ swap_words(ctx->buf, 14); ctx->buf[14] = final_len; ctx->buf[15] = final_len >> 32; /* final result */ md5_mix(ctx, ctx->buf); le32enc(dst + 0, ctx->a); le32enc(dst + 4, ctx->b); le32enc(dst + 8, ctx->c); le32enc(dst + 12, ctx->d); } /* * DigestInfo */ static const struct DigestInfo md5 = { (DigestInitFunc *)md5_reset, (DigestUpdateFunc *)md5_update, (DigestFinalFunc *)md5_final, sizeof(struct md5_ctx), MD5_DIGEST_LENGTH, MD5_BLOCK_LENGTH }; const struct DigestInfo *digest_MD5(void) { return &md5; } pgbouncer-1.7/lib/usual/err.c0000664000175000017500000000505712560223470013133 00000000000000/* * Cmdline error reporting. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #ifndef HAVE_SETPROGNAME static const char *progname; #endif #ifndef HAVE_ERR void err(int e, const char *fmt, ...) { char buf[1024], ebuf[256]; va_list ap; int olderrno = errno; if (fmt) { va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); errx(e, "%s: %s", buf, strerror_r(olderrno, ebuf, sizeof(ebuf))); } else { errx(e, "%s", strerror_r(olderrno, ebuf, sizeof(ebuf))); } } #endif #ifndef HAVE_ERRX void errx(int e, const char *fmt, ...) { va_list ap; if (progname) fprintf(stderr, "%s: ", progname); if (fmt) { va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); } fprintf(stderr, "\n"); exit(e); } #endif #ifndef HAVE_WARN void warn(const char *fmt, ...) { char buf[1024], ebuf[256]; va_list ap; int olderrno = errno; if (fmt) { va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); warnx("%s: %s", buf, strerror_r(olderrno, ebuf, sizeof(ebuf))); } else { warnx("%s", strerror_r(olderrno, ebuf, sizeof(ebuf))); } } #endif #ifndef HAVE_WARNX void warnx(const char *fmt, ...) { va_list ap; if (progname) fprintf(stderr, "%s: ", progname); if (fmt) { va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); } } #endif #ifndef HAVE_SETPROGNAME void setprogname(const char *s) { const char *ss = strrchr(s, '/'); progname = ss ? (ss + 1) : s; } #endif #ifndef HAVE_GETPROGNAME const char *getprogname(void) { return progname; } #endif void *xmalloc(size_t len) { void *p = malloc(len); if (!p) err(1, "no mem"); return p; } void *xrealloc(void *p, size_t len) { void *p2 = realloc(p, len); if (!p2) err(1, "no mem"); return p2; } char *xstrdup(const char *s) { void *s2 = strdup(s); if (!s2) err(1, "no mem"); return s2; } pgbouncer-1.7/lib/usual/mdict.c0000664000175000017500000001424012511203511013423 00000000000000/* * A string to string dictionary. */ #include #include #include #include #include struct MDict { struct CBTree *tree; CxMem *cx; }; struct MDictElem { struct MBuf key; struct MBuf val; }; /* hook for CBTree */ static size_t mdict_getkey(void *ctx, void *obj, const void **dst_p) { struct MDictElem *el = obj; *dst_p = mbuf_data(&el->key); return mbuf_written(&el->key); } static bool mdict_free_obj(void *ctx, void *obj) { struct MDictElem *el = obj; struct MDict *dict = ctx; cx_free(dict->cx, mbuf_data(&el->key)); cx_free(dict->cx, mbuf_data(&el->val)); cx_free(dict->cx, el); return true; } struct MDict *mdict_new(CxMem *cx) { struct MDict *dict; dict = cx_alloc(cx, sizeof(struct MDict)); if (!dict) return NULL; dict->cx = cx; dict->tree = cbtree_create(mdict_getkey, mdict_free_obj, dict, cx); if (!dict->tree) { cx_free(cx, dict); return NULL; } return dict; } void mdict_free(struct MDict *dict) { if (dict) { cbtree_destroy(dict->tree); cx_free(dict->cx, dict); } } const struct MBuf *mdict_get_buf(struct MDict *dict, const char *key, unsigned klen) { struct MDictElem *el = cbtree_lookup(dict->tree, key, klen); if (!el) return NULL; return &el->val; } const char *mdict_get_str(struct MDict *dict, const char *key, unsigned klen) { const struct MBuf *val = mdict_get_buf(dict, key, klen); return val ? mbuf_data(val) : NULL; } bool mdict_put_str(struct MDict *dict, const char *key, unsigned klen, const char *val, unsigned vlen) { char *kptr, *vptr = NULL; struct MDictElem *el; if (val) { vptr = cx_alloc(dict->cx, vlen + 1); if (!vptr) return false; memcpy(vptr, val, vlen); vptr[vlen] = 0; } el = cbtree_lookup(dict->tree, key, klen); if (el) { cx_free(dict->cx, mbuf_data(&el->val)); mbuf_init_fixed_reader(&el->val, vptr, vlen); } else { kptr = cx_alloc(dict->cx, klen + 1); if (!kptr) return false; memcpy(kptr, key, klen); kptr[klen] = 0; el = cx_alloc(dict->cx, sizeof(*el)); if (!el) return false; mbuf_init_fixed_reader(&el->key, kptr, klen); mbuf_init_fixed_reader(&el->val, vptr, vlen); if (!cbtree_insert(dict->tree, el)) return false; } return true; } bool mdict_del_key(struct MDict *dict, const char *key, unsigned klen) { return cbtree_delete(dict->tree, key, klen); } /* * walk over key-val pairs */ struct WalkerCtx { mdict_walker_f cb_func; void *cb_arg; }; static bool walk_helper(void *arg, void *elem) { struct WalkerCtx *ctx = arg; struct MDictElem *el = elem; return ctx->cb_func(ctx->cb_arg, &el->key, &el->val); } bool mdict_walk(struct MDict *dict, mdict_walker_f cb_func, void *cb_arg) { struct WalkerCtx ctx; ctx.cb_func = cb_func; ctx.cb_arg = cb_arg; return cbtree_walk(dict->tree, walk_helper, &ctx); } /* * urldecode */ static int gethex(char c) { if (c >= '0' && c <= '9') return c - '0'; if (c >= 'a' && c <= 'f') return c - 'a' + 10; if (c >= 'A' && c <= 'F') return c - 'A' + 10; return -1; } static void *urldec_str(CxMem *cx, const char **src_p, const char *end, unsigned *len_p) { const char *s; char *d, *dst; int c, len = 0; /* estimate size */ for (s = *src_p; s < end; s++) { if (*s == '%') s += 2; else if (*s == '&' || *s == '=') break; len++; } /* allocate room */ d = dst = cx_alloc(cx, len + 1); if (!dst) return NULL; /* write out */ for (s = *src_p; s < end; ) { if (*s == '%') { if (s + 3 > end) goto err; c = gethex(s[1]) << 4; c |= gethex(s[2]); if (c < 0) goto err; s += 3; *d++ = c; } else if (*s == '+') { *d++ = ' '; s++; } else if (*s == '&' || *s == '=') { break; } else { *d++ = *s++; } } *d = 0; *len_p = d - dst; *src_p = s; return dst; err: cx_free(cx, dst); return NULL; } bool mdict_urldecode(struct MDict *dict, const char *str, unsigned len) { const char *s = str; const char *end = s + len; const char *k, *v; unsigned klen, vlen; struct MDictElem *el; while (s < end) { v = NULL; vlen = 0; el = NULL; /* read key */ k = urldec_str(dict->cx, &s, end, &klen); if (!k) goto fail; /* read value */ if (s < end && *s == '=') { s++; v = urldec_str(dict->cx, &s, end, &vlen); if (!v) goto fail; } if (s < end && *s == '&') s++; /* insert value */ el = cbtree_lookup(dict->tree, k, klen); if (el) { cx_free(dict->cx, mbuf_data(&el->val)); mbuf_init_fixed_reader(&el->val, v, vlen); } else { el = cx_alloc(dict->cx, sizeof(*el)); if (!el) goto fail; mbuf_init_fixed_reader(&el->key, k, klen); mbuf_init_fixed_reader(&el->val, v, vlen); if (!cbtree_insert(dict->tree, el)) goto fail; } } return true; fail: if (k) cx_free(dict->cx, k); if (v) cx_free(dict->cx, v); if (el) cx_free(dict->cx, el); return false; } /* * urlencode */ struct UrlEncCtx { struct MBuf *dst; bool is_first; }; static bool urlenc_str(struct MBuf *dst, const struct MBuf *str) { static const char hextbl[] = "0123456789abcdef"; unsigned len = mbuf_written(str); const unsigned char *s = mbuf_data(str); const unsigned char *end = s + len; bool ok; for (; s < end; s++) { if (*s == ' ') { ok = mbuf_write_byte(dst, '+'); } else if ((*s < 128) && isalnum(*s)) { ok = mbuf_write_byte(dst, *s); } else if (*s == '.' || *s == '_') { ok = mbuf_write_byte(dst, *s); } else { ok = mbuf_write_byte(dst, '%'); ok = ok && mbuf_write_byte(dst, hextbl[*s >> 4]); ok = ok && mbuf_write_byte(dst, hextbl[*s & 15]); } if (!ok) return false; } return true; } static bool urlenc_elem(void *arg, const struct MBuf *key, const struct MBuf *val) { struct UrlEncCtx *ctx = arg; bool ok; if (ctx->is_first) { ctx->is_first = false; } else { ok = mbuf_write_byte(ctx->dst, '&'); if (!ok) return false; } ok = urlenc_str(ctx->dst, key); if (!ok) return false; if (mbuf_data(val) != NULL) { ok = mbuf_write_byte(ctx->dst, '='); if (!ok) return false; ok = urlenc_str(ctx->dst, val); if (!ok) return false; } return true; } bool mdict_urlencode(struct MDict *dict, struct MBuf *dst) { struct UrlEncCtx ctx; ctx.is_first = true; ctx.dst = dst; return mdict_walk(dict, urlenc_elem, &ctx); } pgbouncer-1.7/lib/usual/talloc.h0000664000175000017500000005516612556647420013646 00000000000000/* * talloc - implementation of Talloc API. * * Copyright (c) 2013 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Talloc - hierarchical memory allocator. * * This is reimplementation of Samba's talloc: https://talloc.samba.org/ * * Features: * - Any allocated object can be parent to new objects. * - Any object can have more then one parent. * - References. * - Change parent. * - Built on top of API. * * Missing features: * - Pools. Use pools instead. * * It mostly compatible with original so that Samba's documentation is usable, * but it does not try to be bug-for-bug compatible. */ #ifndef _USUAL_TALLOC_H_ #define _USUAL_TALLOC_H_ #include /* avoid cxalloc.h include */ struct CxMem; /** * Type for untyped pointers that are usable as talloc parent. * * For situations where (void*) needs to be used. There * it might be used to use TALLOC_CTX to document that * talloc pointer is needed. */ typedef void TALLOC_CTX; /** * Destructor signature. * * If it returns -1, talloc_free() cancels its operation * and also returns -1. * * @param ptr Object to be freed. * @returns 0 on success, -1 otherwise. */ typedef int (*talloc_destructor_f)(void *ptr); /** * Give name to "unnamed" allocations. * * By default it generates name that contains * talloc API function name, source file and line number. * * It can be redefined in user source files. */ #ifndef TALLOC_POS #define TALLOC_POS(apifunc) apifunc "@" __FILE__ ":" STR(__LINE__) #endif /** * Free and set pointer to NULL. */ #define TALLOC_FREE(ptr) do { talloc_free(ptr); (ptr) = NULL; } while (0) /* * Internal functions used in macros. */ void *_talloc_const_name(const void *parent, size_t elem_size, size_t count, bool zero_fill, const char *name) _MALLOC; void *_talloc_format_name(const void *parent, size_t elem_size, size_t count, bool zero_fill, const char *fmt, ...) _PRINTF(5,6) _MALLOC; int _talloc_unlink(const void *parent, const void *ptr, const char *source_pos); int _talloc_free(const void *ptr, const char *source_pos); void _talloc_free_children(const void *ptr, const char *source_pos); void *_talloc_realloc(const void *parent, void *ptr, size_t elem_size, size_t count, const char *name); void *_talloc_get_type_abort(const void *ptr, const char *name); void *_talloc_move(const void *new_parent, void **ptr_p); void *_talloc_reference_named(const void *new_parent, const void *ptr, const char *name); /** * @name Allocate object and give name based on C type. * * Object names are set automatically based on C type. * * @{ */ /** * Allocate memory for C type. * * Returned object will have memory for sizeof(type) bytes. * * @param parent Parent context or NULL. * @param #type C type of object. * @returns New object or NULL on error. */ #ifdef DOXYGEN type *talloc(const void *parent, #type); #else #define talloc(parent, type) \ (type *)_talloc_const_name(parent, sizeof(type), 1, false, #type) #endif /** * Allocate zero-filled memory for C type. * * Returned object will have memory for sizeof(type) bytes. * * @param parent Parent context or NULL. * @param #type C type of object. * @returns New object or NULL on error. */ #ifdef DOXYGEN type* talloc_zero(const void *parent, #type); #else #define talloc_zero(parent, type) \ (type *)_talloc_const_name(parent, sizeof(type), 1, true, #type) #endif /** * Allocate array of elements of type given. * * size = count * sizeof(type); * * @param parent Parent context or NULL. * @param type C type. * @param count Number of elements. * @returns New context or NULL on error. */ #ifdef DOXYGEN type *talloc_array(const void *parent, #type, size_t count); #else #define talloc_array(parent, type, count) \ (type *)_talloc_const_name(parent, sizeof(type), count, false, #type) #endif /** * Allocate zero-filled array of elements of type. * * size = count * sizeof(type); * * @param parent Parent context or NULL. * @param type C type. * @param count Number of elements. * @returns New context or NULL on error. */ #ifdef DOXYGEN type *talloc_zero_array(const void *parent, #type, size_t count); #else #define talloc_zero_array(parent, type, count) \ (type *)_talloc_const_name(parent, sizeof(type), count, true, #type) #endif /** * @} * * @name Allocate object and give custom name. * * @{ */ /** * Allocate named object. * * Name pointer is used directly, so it should not change. * * @param parent Parent context or NULL. * @param size Length in bytes. * @param name Pointer to static string, will be used directly. * @returns New context or NULL on error. */ #ifdef DOXYGEN void *talloc_named_const(const void *parent, size_t size, const char *name); #else #define talloc_named_const(parent, size, name) \ _talloc_const_name(parent, size, 1, false, name) #endif /** * Allocate zero-filled memory for named object. * * Name pointer is used directly, so it should not change. * * @param parent Parent context or NULL. * @param size Length in bytes. * @param name Pointer to static string, will be used directly. * @returns New context or NULL on error. */ #ifdef DOXYGEN void *talloc_zero_named_const(const void *parent, size_t size, const char *name); #else #define talloc_zero_named_const(parent, size, name) \ _talloc_const_name(parent, size, 1, true, name) #endif /** * Allocate named context. * * Name is formatted via talloc_vasprintf() and allocated * as child of main object. * * @param parent Parent context or NULL. * @param size Size for allocation. * @param fmt printf format for name. * @returns New context or NULL on error. */ #ifdef DOXYGEN void *talloc_named(const void *parent, size_t size, const char *fmt, ...); #else #define talloc_named(parent, size, ...) \ _talloc_format_name(parent, size, 1, false, __VA_ARGS__) #endif /** * Allocate new top-level named context. * * Name will be allocaten inside context. * * @param fmt Format string for sprintf. * @returns New context or NULL on error. */ #ifdef DOXYGEN void *talloc_init(const char *fmt, ...); #else #define talloc_init(...) \ _talloc_format_name(NULL, 0, 0, false, __VA_ARGS__) #endif /** * @} * * @name Allocate unnamed context. * * The objects will get name based on calling location. * * @{ */ /** * Allocate unnamed context. * * @param parent Parent context or NULL. * @param size Size for allocation. * @returns New pointer or NULL on error. */ #ifdef DOXYGEN void *talloc_size(const void *parent, size_t size); #else #define talloc_size(parent, size) \ _talloc_const_name(parent, size, 1, false, TALLOC_POS("talloc_size")) #endif /** * Allocate unnamed context with zeroed memory. * * @param parent Parent context or NULL. * @param size Size for allocation. * @returns New pointer or NULL on error. */ #ifdef DOXYGEN void *talloc_zero_size(const void *parent, size_t size); #else #define talloc_zero_size(parent, size) \ _talloc_const_name(parent, size, 1, true, TALLOC_POS("talloc_zero_size")) #endif /** * Allocate array of elements of type. * * @param parent Parent context or NULL. * @param size Size for one element. * @param count Number of elements. * @returns New pointer or NULL on error. */ #ifdef DOXYGEN void *talloc_array_size(const void *parent, size_t size, size_t count); #else #define talloc_array_size(parent, size, count) \ _talloc_const_name(parent, size, count, false, TALLOC_POS("talloc_array_size")) #endif /** * Allocate unnamed context from typed pointer. * * sizeof(*(ptr)) will be used as allocation size. * * @param parent Parent context or NULL. * @param ptr Typed pointer * @returns New context or NULL on error. */ #ifdef DOXYGEN type *talloc_ptrtype(const void *parent, type *ptr); #else #define talloc_ptrtype(parent, ptr) \ (typeof(ptr))_talloc_const_name(parent, sizeof(*(ptr)), 1, \ false, TALLOC_POS("talloc_ptrtype")) #endif /** * Allocate array of elements of type taken from pointer. * * size = count * sizeof(*ptr); * * @param parent Parent context or NULL. * @param ptr Typed pointer. * @param count Number of elements. * @returns New context or NULL on error. */ #ifdef DOXYGEN type *talloc_array_ptrtype(const void *parent, type *ptr, size_t count); #else #define talloc_array_ptrtype(parent, ptr, count) \ (typeof(ptr))_talloc_const_name(parent, sizeof(*(ptr)), count, \ false, TALLOC_POS("talloc_array_ptrtype")) #endif /** * Allocate unnamed context as child of parent. * * @param parent Parent context or NULL * @returns New pointer or NULL on error. */ #ifdef DOXYGEN void *talloc_new(const void *parent); #else #define talloc_new(parent) \ _talloc_const_name(parent, 0, 0, false, TALLOC_POS("talloc_new")) #endif /** * @} * * @name Special contexts. * * Contexts that have unusual behaviour. * * @{ */ /** * Allocate context that will be freed on program exit. * * Objects allocated under this context will be freed * via atexit() handler, unless freed earlier. * This is useful to see leaked memory on program exit. * * @returns New context or NULL on error. */ void *talloc_autofree_context(void); /** * Allocate memory from CxMem. * * Returned pointer and all it's children will be allocated from CxMem; * * Name pointer is used directly, so it should not change. * * @param cx CxMem context to allocate from. * @param size Length in bytes. * @param name Pointer to static string, will be used directly. * @returns New context or NULL on error. */ void *talloc_from_cx(const struct CxMem *cx, size_t size, const char *name); /** * Create CxMem context that uses talloc. * * This makes CxMem-based code work with talloc. * * @param parent Parent context or NULL. * @param name Pointer to static string, will be used directly. * @returns New CxMem context or NULL on error. */ const struct CxMem *talloc_as_cx(const void *parent, const char *name); /** * @} * * @name Free memory. * * @{ */ /** * Set function to be called on final free. */ void talloc_set_destructor(const void *ptr, talloc_destructor_f destructor); #ifndef DOXYGEN #define talloc_set_destructor(ptr, dfn) \ do { int (*_dfn)(typeof(ptr)) = (dfn); \ talloc_set_destructor(ptr, (talloc_destructor_f)(_dfn)); \ } while (0) #endif /** * Free allocated context and all it's children. * * This can be called only on context that have one parent. * Use talloc_unlink() if object may have many references. * * @param ptr Pointer to previously allocated area. * @returns 0 on success, -1 on error. */ #ifdef DOXYGEN int talloc_free(const void *ptr); #else #define talloc_free(ptr) \ _talloc_free(ptr, TALLOC_POS("talloc_free")) #endif /** * @} * * @name Reallocate existing context. * * Only context that have only one reference can be reallocated. * * @{ */ /** * Reallocate array. * * @param parent Parent context or NULL. * @param ptr Pointer to be reallocated. * @param #type C type of one element. * @param count Number of elements. * @returns Reallocated context or NULL on error. */ #ifdef DOXYGEN type *talloc_realloc(const void *parent, const void *ptr, #type, size_t count); #else #define talloc_realloc(parent, ptr, type, count) \ (type *)_talloc_realloc(parent, ptr, sizeof(type), count, #type) #endif /** * Reallocate untyped context. * * @param parent Parent context or NULL. * @param ptr Pointer to be reallocated. * @param size New length in bytes. * @returns Reallocated context or NULL on error. */ #ifdef DOXYGEN void *talloc_realloc_size(const void *parent, void *ptr, size_t size); #else #define talloc_realloc_size(parent, ptr, size) \ _talloc_realloc(parent, ptr, size, 1, TALLOC_POS("talloc_realloc_size")) #endif /** * Function version of realloc. * * This is guaranteed to not be macro, * unlike other API calls. * * @param parent Parent context or NULL. * @param ptr Pointer to be reallocated. * @param size New length in bytes. * @returns Reallocated context or NULL on error. */ void *talloc_realloc_fn(const void *parent, void *ptr, size_t size); /** * @} * * @name Custom object name. * * @{ */ /** * Return object name. * * Result will be always non-NULL. */ const char *talloc_get_name(const void *ptr); /** * Set formatted name. * * Format and allocate as child of ptr. * * @param ptr Pointer to be named. * @param fmt Format string. * @returns New name or NULL on error. */ #ifdef DOXYGEN const char *talloc_set_name(const void *ptr, const char *fmt, ...); #else const char *talloc_set_name(const void *ptr, const char *fmt, ...) _PRINTF(2,3); #endif /** * Set name pointer directly. * * @param ptr Pointer to be named. * @param name Pointer to string. */ void talloc_set_name_const(const void *ptr, const char *name); /** * Return same pointer only if name matches, NULL otherwise. */ void *talloc_check_name(const void *ptr, const char *name); /** * @} * * @name Type-based names. * * @{ */ /** * Set context name from type. * * @param ptr Pointer to be named. * @param #type C type. */ #ifdef DOXYGEN void talloc_set_type(const void *ptr, #type); #else #define talloc_set_type(ptr, type) \ talloc_set_name_const(ptr, #type) #endif /** * Get typed pointer only if name matches. * * @param ptr Pointer to be checked. * @param #type C type. */ #ifdef DOXYGEN type *talloc_get_type(const void *ptr, #type); #else #define talloc_get_type(ptr, type) \ (type *)talloc_check_name(ptr, #type) #endif /** * Get typed pointed only if name matches, aborting otherwise. * * This is more extreme version of talloc_get_type(). * * @param ptr Pointer to be checked. * @param #type C type. */ #ifdef DOXYGEN type *talloc_get_type_abort(const void *ptr, #type); #else #define talloc_get_type_abort(ptr, type) \ (type *)_talloc_get_type_abort(ptr, #type) #endif /** * @} * * @name Allocated area size. * * @{ */ /** Get length of allocated area. */ size_t talloc_get_size(const void *ptr); /** * Get number of elements in array. */ #ifdef DOXYGEN size_t talloc_array_length(const type *array); #else #define talloc_array_length(array) (talloc_get_size(array) / sizeof(*(array))) #endif /** * @} * * @name Object parent. * * @{ */ /** Get parent object. */ void *talloc_parent(const void *ptr); /** Get parent object's name. */ const char *talloc_parent_name(const void *ptr); /** Find direct parent based on name */ void *talloc_find_parent_byname(const void *ptr, const char *name); /** Find direct parent based on type */ #ifdef DOXYGEN type *talloc_find_parent_bytype(const void *ptr, #type); #else #define talloc_find_parent_bytype(ptr, type) \ (type *)talloc_find_parent_byname(ptr, #type) #endif /** * @} * * @name Reference handling * * Talloc operates on references, not reference counts. * * @{ */ /** * Create another reference from parent to child. * * @param new_parent New parent context or NULL. * @param ptr Target pointer that is referenced. * @returns Original pointer or NULL on error. */ #ifdef DOXYGEN void *talloc_reference(const void *new_parent, const void *ptr); #else #define talloc_reference(new_parent, ptr) \ (typeof(ptr))_talloc_reference_named(new_parent, ptr, TALLOC_POS("talloc_reference")) #endif /** * Create another reference from NULL context to child. * * @param ptr Target pointer that is referenced. * @returns 0 on success, -1 on error. */ #ifdef DOXYGEN int talloc_increase_ref_count(const void *ptr); #else #define talloc_increase_ref_count(ptr) \ (_talloc_reference_named(NULL, ptr, \ TALLOC_POS("talloc_increase_ref_count")) ? 0 : -1) #endif /** * Remove parent's reference to child. * * If parent is found, unlinks and returns 0, otherwise -1. * * When removing last reference, the object is freed. * * @param parent Parent context or NULL. * @param ptr Pointer to be unlinked. * @returns 0 on success, -1 on error. */ #ifdef DOXYGEN int talloc_unlink(const void *parent, const void *ptr); #else #define talloc_unlink(parent, ptr) \ _talloc_unlink(parent, ptr, TALLOC_POS("talloc_unlink")) #endif /** * Return number of references context has. */ size_t talloc_reference_count(const void *ptr); /** * @} * * @name Change parent * * @{ */ /** * Find reference from old parent switch it to new parent. */ void *talloc_reparent(const void *old_parent, const void *new_parent, const void *ptr); /** * Change primary parent, set old pointer to NULL. * * Useful when moving ponters between structs. * * Cannot be used on pointers that have multiple parents. * * @param new_parent New parent. * @param ptr_p Location of pointer, will be set to NULL if successful. * @returns Original pointer or NULL on error. */ #ifdef DOXYGEN void *talloc_move(const void *new_parent, void **ptr_p); #else #define talloc_move(new_parent, ptr_p) \ (typeof(*(ptr_p)))_talloc_move(new_parent, (void **)(ptr_p)) #endif /** * Change primary parent. * * Cannot be used on pointers that have multiple parents. * * @param new_parent New parent. * @param ptr Pointer to be moved. * @returns Original pointer or NULL on error. */ void *talloc_steal(const void *new_parent, const void *ptr); /** * @} * * @name String functions. * * @{ */ /** Copy memory */ #ifdef DOXYGEN void *talloc_memdup(const void *parent, const void *p, size_t len); #else void *talloc_memdup(const void *parent, const void *p, size_t len) _MALLOC; #endif /** Copy string */ #ifdef DOXYGEN char *talloc_strdup(const void *parent, const char *s); #else char *talloc_strdup(const void *parent, const char *s) _MALLOC; #endif /** Copy string with size limit */ #ifdef DOXYGEN char *talloc_strndup(const void *parent, const char *s, size_t maxlen); #else char *talloc_strndup(const void *parent, const char *s, size_t maxlen) _MALLOC; #endif /** Format string */ #ifdef DOXYGEN char *talloc_asprintf(const void *parent, const char *fmt, ...); #else char *talloc_asprintf(const void *parent, const char *fmt, ...) _PRINTF(2,3) _MALLOC; #endif /** Format string taking argument from va_list */ #ifdef DOXYGEN char *talloc_vasprintf(const void *parent, const char *fmt, va_list ap); #else char *talloc_vasprintf(const void *parent, const char *fmt, va_list ap) _PRINTF(2,0) _MALLOC; #endif /** * @} * * @name String append functions. * * The *_append() functions use strnlen() to get size of string in buffer. * * @{ */ /** Append string to existing string */ char *talloc_strdup_append(char *ptr, const char *s); /** Append string with limit to existing string */ char *talloc_strndup_append(char *ptr, const char *s, size_t maxlen); /** Append formatted string to existing string */ #ifdef DOXYGEN char *talloc_asprintf_append(char *ptr, const char *fmt, ...); #else char *talloc_asprintf_append(char *ptr, const char *fmt, ...) _PRINTF(2,3); #endif /** Append formatted string to existing string */ #ifdef DOXYGEN char *talloc_vasprintf_append(char *ptr, const char *fmt, va_list ap); #else char *talloc_vasprintf_append(char *ptr, const char *fmt, va_list ap) _PRINTF(2,0); #endif /** * @} * * @name String append to complete buffer. * * The *_append_buffer() functions assume talloc_get_size() will * give the string length with final NUL byte. * * @{ */ /** Append string to existing buffer */ char *talloc_strdup_append_buffer(char *ptr, const char *str); /** Append string with limit to existing buffer */ char *talloc_strndup_append_buffer(char *ptr, const char *str, size_t maxlen); /** Append formatted string to existing buffer */ #ifdef DOXYGEN char *talloc_asprintf_append_buffer(char *ptr, const char *fmt, ...); #else char *talloc_asprintf_append_buffer(char *ptr, const char *fmt, ...) _PRINTF(2,3); #endif /** Append formatted string to existing buffer */ #ifdef DOXYGEN char *talloc_vasprintf_append_buffer(char *ptr, const char *fmt, va_list ap); #else char *talloc_vasprintf_append_buffer(char *ptr, const char *fmt, va_list ap) _PRINTF(2,0); #endif /** * @} * * @name Debugging * * @{ */ /** * Set log function. */ void talloc_set_log_fn(void (*log_fn)(const char *message)); /** * Restore default function. */ void talloc_set_log_stderr(void); /** * Set function to be called on abort. */ void talloc_set_abort_fn(void (*abort_fn)(const char *reason)); /** Collect all parent==NULL allocations under one context */ void talloc_enable_null_tracking(void); /** Collect all parent==NULL allocations under one context, but not autofree */ void talloc_enable_null_tracking_no_autofree(void); /** Stop collecting all parent==NULL allocations under one context */ void talloc_disable_null_tracking(void); /** On program exit, run talloc_report(NULL, stderr) */ void talloc_enable_leak_report(void); /** On program exit, run talloc_report_full(NULL, stderr) */ void talloc_enable_leak_report_full(void); /** * Return allocated bytes under context. */ size_t talloc_total_size(const void *ptr); /** * Return number of contexts under context. */ size_t talloc_total_blocks(const void *ptr); /** * Walk parents */ bool talloc_is_parent(const void *parent, const void *ptr); /** Print report about context and it's immediate childs */ void talloc_report(const void *ptr, FILE *f); /** Print report about context and it's all childs */ void talloc_report_full(const void *ptr, FILE *f); /** Print full report about context, with customizable depth */ void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f); /** Run callback on context and it's children */ void talloc_report_depth_cb(const void *ptr, int depth, int max_depth, void (*callback)(const void *ptr, int depth, int max_depth, int is_ref, void *private_data), void *private_data); /** Print parents to file */ void talloc_show_parents(const void *ptr, FILE *file); /** * Activate memory limit for object. * * The limit affects only new children allocated after setting it. * It does not take account object itself and it's current children. * * @param ptr Targer pointer. * @param max_size Limit in bytes. * @returns 0 on success, -1 otherwise. */ int talloc_set_memlimit(const void *ptr, size_t max_size); /** @} */ /* deprecated */ #define talloc_free_children(ptr) _talloc_free_children(ptr, TALLOC_POS("talloc_free_children")) /* custom */ void talloc_set_debug(int level); #endif pgbouncer-1.7/lib/usual/ctype.h0000664000175000017500000000575212511203511013464 00000000000000/* * ctype wrappers * * Copyright (c) 2011 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * ctype compat. * * Provides wrappers that make sure the functions work on 'char' values. * * @note * POSIX requires that these functions accept EOF/-1 in addition * to ordinary byte values. That means when working on 'char', * the functions cannot differetiate between 0xFF and EOF. * As no code should give EOF to functions and no code * should depend whether 0xFF is labeled ispunct() or not, * it seems no worthwhile to fix it. */ #ifndef _USUAL_CTYPE_H_ #define _USUAL_CTYPE_H_ #include #include #ifndef isblank #define isblank usual_isblank static inline int isblank(int c) { return (c == ' ') || (c == '\t'); } #endif /* keep right signature, cast to uchar internally */ #define _WRAP_CTYPE_FN(name) \ static inline int safe_ ## name (int c) { \ return name((unsigned char)(c)); \ } _WRAP_CTYPE_FN(isalnum) #undef isalnum /** Safe isalnum */ #define isalnum safe_isalnum _WRAP_CTYPE_FN(isalpha) #undef isalpha /** Safe isalpha */ #define isalpha safe_isalpha _WRAP_CTYPE_FN(isascii) #undef isascii /** Safe isascii */ #define isascii safe_isascii _WRAP_CTYPE_FN(isblank) #undef isblank /** Safe isblank */ #define isblank safe_isblank _WRAP_CTYPE_FN(iscntrl) #undef iscntrl /** Safe iscntrl */ #define iscntrl safe_iscntrl _WRAP_CTYPE_FN(isdigit) #undef isdigit /** Safe isdigit */ #define isdigit safe_isdigit _WRAP_CTYPE_FN(isgraph) #undef isgraph /** Safe isgraph */ #define isgraph safe_isgraph _WRAP_CTYPE_FN(islower) #undef islower /** Safe islower */ #define islower safe_islower _WRAP_CTYPE_FN(isprint) #undef isprint /** Safe isprint */ #define isprint safe_isprint _WRAP_CTYPE_FN(ispunct) #undef ispunct /** Safe ispunct */ #define ispunct safe_ispunct _WRAP_CTYPE_FN(isspace) #undef isspace /** Safe isspace */ #define isspace safe_isspace _WRAP_CTYPE_FN(isupper) #undef isupper /** Safe isupper */ #define isupper safe_isupper _WRAP_CTYPE_FN(isxdigit) #undef isxdigit /** Safe isxdigit */ #define isxdigit safe_isxdigit _WRAP_CTYPE_FN(tolower) #undef tolower /** Safe tolower */ #define tolower safe_tolower _WRAP_CTYPE_FN(toupper) #undef toupper /** Safe toupper */ #define toupper safe_toupper #undef _WRAP_CTYPE_FN #endif /* _USUAL_CTYPE_H_ */ pgbouncer-1.7/lib/usual/string.c0000664000175000017500000003040212615704017013643 00000000000000/* * String utilities. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #ifdef HAVE_XLOCALE_H #include #endif #ifdef HAVE_LANGINFO_H #include #endif /* * Dynamic list of strings. */ struct StrList { struct StatList list; CxMem *ca; }; struct StrItem { struct List node; const char *str; }; bool strlist_empty(struct StrList *slist) { return statlist_empty(&slist->list); } bool strlist_append(struct StrList *slist, const char *str) { const char *nstr = NULL; bool ok; if (str) { nstr = cx_strdup(slist->ca, str); if (!nstr) return false; } ok = strlist_append_ref(slist, nstr); if (!ok) cx_free(slist->ca, nstr); return ok; } bool strlist_append_ref(struct StrList *slist, const char *str) { struct StrItem *item = cx_alloc(slist->ca, sizeof(*item)); if (!item) return false; list_init(&item->node); item->str = str; statlist_append(&slist->list, &item->node); return true; } const char *strlist_pop(struct StrList *slist) { struct StrItem *item; struct List *el; const char *str; el = statlist_pop(&slist->list); if (!el) return NULL; item = container_of(el, struct StrItem, node); str = item->str; cx_free(slist->ca, item); return str; } struct StrList *strlist_new(CxMem *ca) { struct StrList *slist = cx_alloc0(ca, sizeof(*slist)); if (!slist) return NULL; statlist_init(&slist->list, "strlist"); slist->ca = ca; return slist; } void strlist_free(struct StrList *slist) { const char *s; if (!slist) return; while (!strlist_empty(slist)) { s = strlist_pop(slist); if (s) cx_free(slist->ca, s); } cx_free(slist->ca, slist); } bool strlist_foreach(const struct StrList *slist, str_cb func, void *arg) { struct List *el; struct StrItem *item; statlist_for_each(el, &slist->list) { item = container_of(el, struct StrItem, node); if (!func(arg, item->str)) return false; } return true; } /* * Parse comma separated words. */ static inline const char *skip_ws(const char *p) { while (*p && isspace(*p)) p++; return p; } bool parse_word_list(const char *s, str_cb cb_func, void *cb_arg) { struct MBuf buf; const char *p = s; const char *start, *end; mbuf_init_dynamic(&buf); while (*p) { /* parse word */ p = skip_ws(p); start = p; while (*p && *p != ',') p++; end = p; while (end > start && isspace(*(end - 1))) end--; /* parse comma */ if (*p) { if (*p == ',') { p++; } else { goto failed_syntax; } } /* extract string */ if (!mbuf_write(&buf, start, end - start)) goto failed; if (!mbuf_write_byte(&buf, 0)) goto failed; /* launch callback */ if (!cb_func(cb_arg, (const char *)buf.data)) goto failed; /* reset */ mbuf_rewind_writer(&buf); } mbuf_free(&buf); return true; failed_syntax: errno = EINVAL; failed: mbuf_free(&buf); return false; } /* * Minimal spec-conforming implementations of strlcpy(), strlcat(). */ #ifndef HAVE_STRLCPY size_t strlcpy(char *dst, const char *src, size_t n) { size_t len = strlen(src); if (len < n) { memcpy(dst, src, len + 1); } else if (n > 0) { memcpy(dst, src, n - 1); dst[n - 1] = 0; } return len; } #endif #ifndef HAVE_STRLCAT size_t strlcat(char *dst, const char *src, size_t n) { size_t pos = 0; while (pos < n && dst[pos]) pos++; return pos + strlcpy(dst + pos, src, n - pos); } #endif char *strpcpy(char *dst, const char *src, size_t n) { if (n == 0) return NULL; for (; n > 0; n--, dst++, src++) { if ((*dst = *src) == '\0') return dst; } dst[-1] = '\0'; return NULL; } char *strpcat(char *dst, const char *src, size_t n) { size_t dstlen = strnlen(dst, n); if (dstlen < n) return strpcpy(dst + dstlen, src, n - dstlen); return NULL; } #ifndef HAVE_MEMPCPY void *mempcpy(void *dst, const void *src, size_t n) { memcpy(dst, src, n); return (char *)(dst) + n; } #endif #ifndef HAVE_MEMRCHR void *memrchr(const void *s, int c, size_t n) { const uint8_t *p = s; while (n--) { if (p[n] == c) return (void *)(p + n); } return NULL; } #endif #ifndef HAVE_MEMMEM void *memmem(const void *haystack, size_t hlen, const void *needle, size_t nlen) { const uint8_t *s = haystack; const uint8_t *q = needle; const uint8_t *s2; size_t i; if (nlen == 0) return (void *)haystack; if (nlen > hlen) return NULL; s2 = memchr(haystack, *q, hlen); if (!s2 || nlen == 1) return (void *)s2; for (i = s2 - s; i <= hlen - nlen; i++) { if (s[i] == q[0] && s[i+1] == q[1]) { if (memcmp(s + i + 2, q + 2, nlen - 2) == 0) return (void *)(s + i); } } return NULL; } #endif #ifndef HAVE_EXPLICIT_BZERO #if defined(_WIN32) && defined(SecureZeroMemory) void explicit_bzero(void *buf, size_t len) { SecureZeroMemory(buf, len); } #elif defined(HAVE_MEMSET_S) void explicit_bzero(void *buf, size_t len) { memset_s(buf, len, 0, len); } #else /* non-win32 */ /* avoid link-time optimization */ #if defined(__GNUC__x) || __has_attribute(weak) void __explicit_bzero_hack(void *, size_t); __attribute__((weak)) void __explicit_bzero_hack(void *buf, size_t len) { } #else typedef void (*__explicit_bzero_cb_t)(void *, size_t); static void __explicit_bzero_hack_cb(void *buf, size_t len) { } static volatile __explicit_bzero_cb_t __explicit_bzero_hack = __explicit_bzero_hack_cb; #endif void explicit_bzero(void *buf, size_t len) { memset(buf, 0, len); __explicit_bzero_hack(buf, len); } #endif #endif /* !_WIN32 */ #ifndef HAVE_BASENAME const char *basename(const char *path) { const char *p, *p2; static char buf[256]; unsigned len; if (path == NULL || path[0] == 0) return memcpy(buf, ".", 2); if ((p = strrchr(path, '/')) == NULL) return path; if (p[1]) return p + 1; /* last char is '/' */ for (p2 = p; p2 > path; p2--) { if (p2[-1] != '/') { len = p2 - path; if (len > sizeof(buf) - 1) len = sizeof(buf) - 1; memcpy(buf, p2 - len, len); buf[len] = 0; return basename(buf); } } /* path contains only '/' chars */ return p; } #endif #ifndef HAVE_DIRNAME const char *dirname(const char *path) { const char *p; size_t len; static char buf[1024]; if (path == NULL || path[0] == 0) return memcpy(buf, ".", 2); /* ignore tailing '/' */ len = strlen(path); while (len && path[len - 1] == '/') len--; if (!len) return memcpy(buf, "/", 2); /* find end of dirname, strip '/' */ if ((p = memrchr(path, '/', len)) == NULL) return memcpy(buf, ".", 2); len = p - path; while (len && path[len - 1] == '/') len--; if (!len) return memcpy(buf, "/", 2); /* return it */ if (len > sizeof(buf) - 1) { errno = ENAMETOOLONG; return NULL; } memcpy(buf, path, len); buf[len] = 0; return buf; } #endif #ifdef WIN32 const char *win32_strerror(int e) { static char buf[1024]; return strerror_r(e, buf, sizeof(buf)); } #endif /* restore original strerror_r() */ #undef strerror_r const char *usual_strerror_r(int e, char *dst, size_t dstlen) { #ifdef WIN32 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, e, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), dst, dstlen, NULL); #else /* !WIN32 */ #ifdef STRERROR_R_CHAR_P dst = strerror_r(e, dst, dstlen); #else if (strerror_r(e, dst, dstlen) != 0) strlcpy(dst, "ERR", dstlen); #endif #endif /* !WIN32 */ return dst; } void *mempbrk(const void *data, size_t dlen, const void *find, size_t flen) { const uint8_t *s = data; const uint8_t *fb = find; size_t i; struct Bitmap256 bmap; if (flen == 0) return NULL; if (flen == 1) return memchr(data, fb[0], dlen); bitmap256_init(&bmap); for (i = 0; i < flen; i++) bitmap256_set(&bmap, fb[i]); for (i = 0; i < dlen; i++) { if (bitmap256_is_set(&bmap, s[i])) return (void *)(s + i); } return NULL; } size_t memspn(const void *data, size_t dlen, const void *accept, size_t alen) { const uint8_t *s = data; const uint8_t *fb = accept; size_t i; struct Bitmap256 bmap; if (alen == 0) return 0; if (alen == 1) { for (i = 0; i < dlen; i++) if (s[i] != fb[0]) break; return i; } bitmap256_init(&bmap); for (i = 0; i < alen; i++) bitmap256_set(&bmap, fb[i]); for (i = 0; i < dlen; i++) { if (!bitmap256_is_set(&bmap, s[i])) break; } return i; } size_t memcspn(const void *data, size_t dlen, const void *reject, size_t rlen) { const void *p; p = mempbrk(data, dlen, reject, rlen); if (p != NULL) return (char *)p - (char *)data; return dlen; } double strtod_dot(const char *s, char **tokend) { const char *dp; char buf[128]; char *dst, *tmp, *end, *dot = NULL; unsigned int i, dplen; double res; /* check if locale is sane */ #ifdef HAVE_NL_LANGINFO dp = nl_langinfo(RADIXCHAR); #else dp = localeconv()->decimal_point; #endif if (memcmp(dp, ".", 2) == 0) return strtod(s, tokend); /* try to use proper api */ #ifdef HAVE_STRTOD_L { static locale_t c_locale = NULL; if (!c_locale) c_locale = newlocale(LC_ALL_MASK, "C", NULL); if (c_locale) return strtod_l(s, tokend, c_locale); } #endif while (*s && isspace(*s)) s++; dot = NULL; dst = buf; end = buf + sizeof(buf) - 5; dplen = dp[1] ? strlen(dp) : 1; for (i = 0; s[i]; i++) { if (s[i] >= '0' && s[i] <= '9') { *dst++ = s[i]; } else if (s[i] == '.') { dot = dst; memcpy(dst, dp, dplen); dst += dplen; } else if (s[i] == '-' || s[i] == '+' || s[i] == 'e' || s[i] == 'E') { *dst++ = s[i]; } else { break; } if (dst >= end) { errno = ERANGE; return 0; } } *dst = '\0'; if (!dot) return strtod(s, tokend); tmp = NULL; res = strtod(buf, &tmp); if (tmp && tokend) { *tokend = (char *)s + (tmp - buf); if (dot && tmp > dot && dplen > 1) *tokend -= (dplen - 1); } return res; } ssize_t dtostr_dot(char *buf, size_t buflen, double val) { const char *dp; ssize_t len, dplen; char *p; /* render with max precision */ len = snprintf(buf, buflen, "%.17g", val); if (len >= (ssize_t)buflen || len < 0) return len; /* check if locale is sane */ #ifdef HAVE_NL_LANGINFO dp = nl_langinfo(RADIXCHAR); #else dp = localeconv()->decimal_point; #endif if (memcmp(dp, ".", 2) == 0) return len; dplen = dp[1] ? strlen(dp) : 1; p = memchr(buf, dp[0], len); if (p) { *p = '.'; if (dp[1]) { memmove(p + 1, p + dplen, strlen(p + dplen) + 1); len -= dplen - 1; } } return len; } #ifndef HAVE_STRTONUM long long strtonum(const char *s, long long minval, long long maxval, const char **errstr_p) { char *end = NULL; long long res; int old_errno = errno; if (minval > maxval) goto einval; errno = 0; res = strtoll(s, &end, 10); if (errno == ERANGE) { if (res < 0) goto esmall; else goto elarge; } else if (errno != 0) { goto einval; } else if (*end || end == s) { goto einval; } else if (res < minval) { goto esmall; } else if (res > maxval) { goto elarge; } /* success */ if (errstr_p) *errstr_p = NULL; errno = old_errno; return res; esmall: if (errstr_p) *errstr_p = "too small"; errno = ERANGE; return 0; elarge: if (errstr_p) *errstr_p = "too large"; errno = ERANGE; return 0; einval: if (errstr_p) *errstr_p = "invalid"; errno = EINVAL; return 0; } #endif #ifndef HAVE_STRSEP char *strsep(char **stringp, const char *delim) { char *end, *start = *stringp; if (start) { end = start + strcspn(start, delim); *stringp = *end ? end + 1 : NULL; *end = 0; } return start; } #endif #ifndef HAVE_ASPRINTF int asprintf(char **dst_p, const char *fmt, ...) { int res; va_list ap; va_start(ap, fmt); res = vasprintf(dst_p, fmt, ap); va_end(ap); return res; } #endif #ifndef HAVE_VASPRINTF int vasprintf(char **dst_p, const char *fmt, va_list ap) { return cx_vasprintf(NULL, dst_p, fmt, ap); } #endif pgbouncer-1.7/lib/usual/daemon.c0000664000175000017500000001076312511202014013571 00000000000000/* * Daemonization & pidfile handling. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include /* * pidfile management. */ static char *g_pidfile; static void remove_pidfile(void) { if (!g_pidfile) return; unlink(g_pidfile); free(g_pidfile); g_pidfile = NULL; } /* * Reads pid from pidfile and sends a signal to it. * * true - signaling was successful. * false - ENOENT / ESRCH * * fatal() otherwise. */ bool signal_pidfile(const char *pidfile, int sig) { char buf[128 + 1]; struct stat st; pid_t pid = 0; int fd, res; if (!pidfile || !pidfile[0]) return false; intr_loop: /* check if pidfile exists */ if (stat(pidfile, &st) < 0) goto fail; /* read old pid */ fd = open(pidfile, O_RDONLY); if (fd < 0) goto fail; res = read(fd, buf, sizeof(buf) - 1); close(fd); if (res <= 0) goto fail; /* parse pid */ buf[res] = 0; errno = 0; pid = strtoul(buf, NULL, 10); if (errno) { /* should we panic, or say no such process exists? */ if (0) errno = ESRCH; goto fail; } /* send the signal */ res = kill(pid, sig); if (res == 0) return true; fail: /* decide error seriousness */ if (errno == EINTR) goto intr_loop; if (errno == ENOENT || errno == ESRCH) return false; fatal_perror("signal_pidfile: Unexpected error"); } static void check_pidfile(const char *pidfile) { if (signal_pidfile(pidfile, 0)) fatal("pidfile exists, another instance running?"); if (errno == ESRCH) { log_info("Stale pidfile, removing"); unlink(pidfile); } } static void write_pidfile(const char *pidfile, bool first_write) { char buf[64]; pid_t pid; int res, fd, len; static int atexit_hook = 0; int flags = O_WRONLY | O_CREAT; if (!pidfile || !pidfile[0]) return; if (g_pidfile) free(g_pidfile); g_pidfile = strdup(pidfile); if (!g_pidfile) fatal_perror("cannot alloc pidfile"); pid = getpid(); snprintf(buf, sizeof(buf), "%u\n", (unsigned)pid); /* don't allow overwrite on first write */ if (first_write) flags |= O_EXCL; fd = open(pidfile, flags, 0644); if (fd < 0) fatal_perror("Cannot write pidfile: '%s'", pidfile); len = strlen(buf); loop: res = write(fd, buf, len); if (res < 0) { if (errno == EINTR) goto loop; fatal_perror("Write to pidfile failed: '%s'", pidfile); } else if (res < len) { len -= res; goto loop; } close(fd); if (!atexit_hook) { /* only remove when we have it actually written */ atexit(remove_pidfile); atexit_hook = 1; } } /* * Function: daemonize * * Handle pidfile and daemonization. * * If pidfile is given, check if old process is running. * * If going background is required, require non-empty pidfile * and logfile. Then fork to background and write pidfile. */ void daemonize(const char *pidfile, bool go_background) { int pid, fd; if (pidfile && pidfile[0]) { check_pidfile(pidfile); /* write pidfile twice, to be able to show problems to user */ write_pidfile(pidfile, true); } else if (go_background) fatal("daemon needs pidfile configured"); if (!go_background) return; if ((!cf_logfile || !cf_logfile[0]) && !cf_syslog) fatal("daemon needs logging configured"); /* send stdin, stdout, stderr to /dev/null */ fd = open("/dev/null", O_RDWR); if (fd < 0) fatal_perror("/dev/null"); dup2(fd, 0); dup2(fd, 1); dup2(fd, 2); if (fd > 2) close(fd); /* fork new process */ pid = fork(); if (pid < 0) fatal_perror("fork"); if (pid > 0) _exit(0); /* create new session */ pid = setsid(); if (pid < 0) fatal_perror("setsid"); /* fork again to avoid being session leader */ pid = fork(); if (pid < 0) fatal_perror("fork"); if (pid > 0) _exit(0); write_pidfile(pidfile, false); } pgbouncer-1.7/lib/usual/socket.c0000664000175000017500000002504012511203511013613 00000000000000/* * Socket helpers and compat. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #ifdef HAVE_UCRED_H #include #endif #ifdef HAVE_SYS_UCRED_H #include #endif /* toggle non-blocking flag */ bool socket_set_nonblocking(int fd, bool non_block) { int flags; /* get old flags */ flags = fcntl(fd, F_GETFL, 0); if (flags < 0) return false; /* flip O_NONBLOCK */ if (non_block) flags |= O_NONBLOCK; else flags &= ~O_NONBLOCK; /* set new flags */ if (fcntl(fd, F_SETFL, flags) < 0) return false; return true; } /* initial socket setup */ bool socket_setup(int sock, bool non_block) { int res; #ifdef SO_NOSIGPIPE /* disallow SIGPIPE, if possible */ int val = 1; res = setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, &val, sizeof(val)); if (res < 0) return false; #endif /* close fd on exec */ res = fcntl(sock, F_SETFD, FD_CLOEXEC); if (res < 0) return false; /* when no data available, return EAGAIN instead blocking */ if (!socket_set_nonblocking(sock, non_block)) return false; return true; } bool socket_set_keepalive(int fd, int onoff, int keepidle, int keepintvl, int keepcnt) { int val, res; if (!onoff) { /* turn keepalive off */ val = 0; res = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)); return (res == 0); } /* turn keepalive on */ val = 1; res = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)); if (res < 0) return false; /* Darwin */ #ifdef TCP_KEEPALIVE if (keepidle) { val = keepidle; res = setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &val, sizeof(val)); if (res < 0 && errno != ENOPROTOOPT) return false; } #endif /* Linux, NetBSD */ #ifdef TCP_KEEPIDLE if (keepidle) { val = keepidle; res = setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)); if (res < 0 && errno != ENOPROTOOPT) return false; } #endif #ifdef TCP_KEEPINTVL if (keepintvl) { val = keepintvl; res = setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)); if (res < 0 && errno != ENOPROTOOPT) return false; } #endif #ifdef TCP_KEEPCNT if (keepcnt > 0) { val = keepcnt; res = setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)); if (res < 0 && errno != ENOPROTOOPT) return false; } #endif /* Windows */ #ifdef SIO_KEEPALIVE_VALS if (keepidle || keepintvl) { struct tcp_keepalive vals; DWORD outlen = 0; if (!keepidle) keepidle = 5 * 60; if (!keepintvl) keepintvl = 15; vals.onoff = 1; vals.keepalivetime = keepidle * 1000; vals.keepaliveinterval = keepintvl * 1000; res = WSAIoctl(fd, SIO_KEEPALIVE_VALS, &vals, sizeof(vals), NULL, 0, &outlen, NULL, NULL); if (res != 0) return false; } #endif return true; } /* * Convert sockaddr to string. Supports ipv4, ipv6 and unix sockets. */ const char *sa2str(const struct sockaddr *sa, char *dst, int dstlen) { const struct sockaddr_in *in; const struct sockaddr_in6 *in6; const struct sockaddr_un *un; const char *tmp; char buf[128]; switch (sa->sa_family) { case AF_INET: in = (struct sockaddr_in *)sa; tmp = inet_ntop(AF_INET, &in->sin_addr, buf, sizeof(buf)); if (!tmp) return NULL; snprintf(dst, dstlen, "%s:%d", tmp, ntohs(in->sin_port)); break; case AF_INET6: in6 = (struct sockaddr_in6 *)sa; tmp = inet_ntop(AF_INET6, &in6->sin6_addr, buf, sizeof(buf)); if (!tmp) return NULL; snprintf(dst, dstlen, "%s/%d", tmp, ntohs(in6->sin6_port)); break; case AF_UNIX: un = (struct sockaddr_un *)sa; snprintf(dst, dstlen, "unix:%s", un->sun_path); break; default: snprintf(dst, dstlen, "sa2str(%d): unknown proto", sa->sa_family); break; } return dst; } #ifndef HAVE_GETPEEREID /* * Get other side's uid and gid for UNIX socket. */ int getpeereid(int fd, uid_t *uid_p, gid_t *gid_p) { pid_t pid; return getpeercreds(fd, uid_p, gid_p, &pid); } #endif /* * Get uid, gid and pid of unix socket peer. * * Pid may not be availalbe on some OSes. * It's set to 0 then. */ int getpeercreds(int fd, uid_t *uid_p, gid_t *gid_p, pid_t *pid_p) { /* What a mess */ #if defined(SO_PEERCRED) /* linux and others */ #if defined(HAVE_SYS_UCRED_H) struct sockpeercred cred; /* openbsd */ #else struct ucred cred; /* linux */ #endif socklen_t len = sizeof(cred); if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &len) == 0) { *uid_p = cred.uid; *gid_p = cred.gid; *pid_p = cred.pid; return 0; } return -1; #elif defined(HAVE_GETPEERUCRED) /* solaris */ ucred_t *cred = NULL; if (getpeerucred(fd, &cred) == 0) { *uid_p = ucred_geteuid(cred); *gid_p = ucred_getegid(cred); *pid_p = ucred_getpid(cred); ucred_free(cred); if ((int)*uid_p == -1 || (int)*gid_p == -1) return -1; return 0; } return -1; #elif defined(LOCAL_PEEREID) /* netbsd */ struct unpcbid cred; socklen_t len = sizeof(cred); if (getsockopt(fd, 0, LOCAL_PEEREID, &cred, &len) != 0) return -1; *uid_p = cred.unp_euid; *gid_p = cred.unp_egid; *pid_p = cred.unp_pid; return 0; #elif defined(HAVE_GETPEEREID) /* generic bsd; no pid */ *pid_p = 0; return getpeereid(fd, uid_p, gid_p) == 0 ? 0 : -1; #elif defined(LOCAL_PEERCRED) /* freebsd, osx, dfly; no pid */ struct xucred cred; socklen_t len = sizeof(cred); if (getsockopt(fd, 0, LOCAL_PEERCRED, &cred, &len) != 0) return -1; if (cred.cr_version != XUCRED_VERSION) { errno = EINVAL; return -1; } *uid_p = cred.cr_uid; *gid_p = cred.cr_gid; *pid_p = 0; return 0; #else /* no implementation */ errno = ENOSYS; return -1; #endif } #ifndef HAVE_POLL /* * Emulate poll() with select() */ #ifdef HAVE_SYS_SELECT_H #include #endif /* * dynamic buffer for fd_set to avoid depending on FD_SETSIZE */ struct fd_buf { fd_set *set; int alloc_bytes; }; static void fdbuf_zero(struct fd_buf *buf) { if (buf->set) memset(buf->set, 0, buf->alloc_bytes); } static bool fdbuf_resize(struct fd_buf *buf, int fd) { int need_bytes; unsigned char *ptr; /* default allocation */ int alloc = sizeof(fd_set); #ifdef WIN32 int cnt = buf->set ? buf->set->fd_count : 0; /* win32 fd_set is array of handles, +8 for count&padding */ need_bytes = (cnt + 1) * sizeof(buf->set->fd_array[0]) + 8; #else /* otherwise, fd_set is bitmap, +8 for int/long alignment */ need_bytes = fd / 8 + 8; #endif if (buf->alloc_bytes < need_bytes) { while (alloc < need_bytes) alloc *= 2; if (!buf->set) ptr = malloc(alloc); else ptr = realloc(buf->set, alloc); if (!ptr) return false; /* clean new area */ memset(ptr + buf->alloc_bytes, 0, alloc - buf->alloc_bytes); buf->set = (fd_set *)ptr; buf->alloc_bytes = alloc; } return true; } /* win32: make macros ignore FD_SETSIZE */ #undef FD_SETSIZE #define FD_SETSIZE (1 << 30) int poll(struct pollfd *fds, nfds_t nfds, int timeout_ms) { static struct fd_buf readfds = { NULL, 0 }; static struct fd_buf writefds = { NULL, 0 }; struct pollfd *pf; int res, fd_max = 0; struct timeval *tv = NULL; struct timeval tvreal; unsigned i; /* convert timeout_ms to timeval */ if (timeout_ms >= 0) { tvreal.tv_sec = timeout_ms / 1000; tvreal.tv_usec = (timeout_ms % 1000) * 1000; tv = &tvreal; } else if (timeout_ms < -1) goto err_inval; /* * Convert pollfds to fd sets. */ fdbuf_zero(&readfds); fdbuf_zero(&writefds); for (i = 0; i < nfds; i++) { pf = fds + i; if (pf->fd < 0) goto err_badf; /* sets must be equal size */ if (!fdbuf_resize(&readfds, pf->fd)) goto err_nomem; if (!fdbuf_resize(&writefds, pf->fd)) goto err_nomem; if (pf->events & POLLIN) FD_SET((unsigned)pf->fd, readfds.set); if (pf->events & POLLOUT) FD_SET((unsigned)pf->fd, writefds.set); if (pf->fd > fd_max) fd_max = pf->fd; } res = select(fd_max + 1, readfds.set, writefds.set, NULL, tv); if (res <= 0) return res; /* * select() and poll() count fd-s differently, * need to recount them here. */ res = 0; for (i = 0; i < nfds; i++) { pf = fds + i; pf->revents = 0; if ((pf->events & POLLIN) && FD_ISSET(pf->fd, readfds.set)) pf->revents |= POLLIN; if ((pf->events & POLLOUT) && FD_ISSET(pf->fd, writefds.set)) pf->revents |= POLLOUT; if (pf->revents) res += 1; } return res; err_nomem: errno = ENOMEM; return -1; err_badf: errno = EBADF; return -1; err_inval: errno = EINVAL; return -1; } #endif /* PLPROXY_POLL_COMPAT */ #ifdef WIN32 /* create local TCP socket, idea from libevent/Tor */ int win32_socketpair(int d, int typ, int proto, int sv[2]) { int list = -1, s1 = -1, s2 = -1; struct sockaddr_in sa1, sa2; socklen_t slen = sizeof(sa1); int res; if (d != AF_INET && d != AF_UNIX) goto err_inval; if (proto || !sv) goto err_inval; /* prepare sockaddr for bind */ memset(&sa1, 0, sizeof(sa1)); sa1.sin_family = AF_INET; sa1.sin_addr.s_addr = htonl(INADDR_LOOPBACK); sa1.sin_port = htons(0); /* create listen socket */ list = socket(AF_INET, typ, 0); if (list == -1) return -1; res = bind(list, (struct sockaddr *)&sa1, sizeof(sa1)); if (res == -1) goto failed; res = listen(list, 1); if (res == -1) goto failed; /* read listen port */ res = getsockname(list, (struct sockaddr *)&sa1, &slen); if (res == -1 || slen != sizeof(sa1)) goto failed; /* connect to it */ s1 = socket(AF_INET, typ, 0); if (s1 == -1) goto failed; res = connect(s1, (struct sockaddr *)&sa1, sizeof(sa1)); if (res == -1) goto failed; /* and accept from other end */ s2 = accept(list, (struct sockaddr *)&sa2, &slen); if (s2 == -1 || slen != sizeof(sa2)) goto failed; /* sanity check */ res = getsockname(s1, (struct sockaddr *)&sa1, &slen); if (res == -1 || slen != sizeof(sa1)) goto failed; if (sa1.sin_port != sa2.sin_port) goto failed; closesocket(list); sv[0] = s1; sv[1] = s2; return 0; failed: errno = (res == -1) ? WSAGetLastError() : EFAULT; if (list != -1) closesocket(list); if (s1 != -1) closesocket(s1); if (s2 != -1) closesocket(s2); return -1; err_inval: errno = EINVAL; return -1; } #endif pgbouncer-1.7/lib/usual/misc.h0000664000175000017500000000307712511202014013266 00000000000000/* * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * Random stuff that does not fit elsewhere. */ #ifndef _USUAL_MISC_H_ #define _USUAL_MISC_H_ #include #ifdef WORDS_BIGENDIAN #define FOURCC(a,b,c,d) \ ( ((unsigned int)(unsigned char)(a) << 24) \ | ((unsigned int)(unsigned char)(b) << 16) \ | ((unsigned int)(unsigned char)(c) << 8) \ | ((unsigned int)(unsigned char)(d))) #else /** Four-byte identifier as integer */ #define FOURCC(a,b,c,d) \ ( ((unsigned int)(unsigned char)(a)) \ | ((unsigned int)(unsigned char)(b) << 8) \ | ((unsigned int)(unsigned char)(c) << 16) \ | ((unsigned int)(unsigned char)(d) << 24)) #endif #if defined(__i386__) || defined(__x86_64__) #define mb() asm volatile("mfence":::"memory") #define rmb() asm volatile("lfence":::"memory") #define wmb() asm volatile("sfence":::"memory") #endif #endif pgbouncer-1.7/lib/usual/cbtree.h0000664000175000017500000000441112511203511013573 00000000000000/* * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * * Crit-bit tree / binary radix tree. */ #ifndef _USUAL_CBTREE_H_ #define _USUAL_CBTREE_H_ #include /** returns length of the key */ typedef size_t (*cbtree_getkey_func)(void *ctx, void *obj, const void **dst_p); /** walk over tree */ typedef bool (*cbtree_walker_func)(void *ctx, void *obj); /** Handle to tree */ struct CBTree; /** * Create new tree. * * @param obj_key_cb callback to get the key for a object * @param obj_free_cb callback to free the object when tree node is freed (optional) * @param cb_ctx extra pointer passed to callbacks * @param cx memory context where from allocate */ struct CBTree *cbtree_create(cbtree_getkey_func obj_key_cb, cbtree_walker_func obj_free_cb, void *cb_ctx, CxMem *cx); /** * frees all resources allocated. * If obj_free_cb is non-NULL, it will be called per each object. */ void cbtree_destroy(struct CBTree *tree); /** Inserts new node to tree */ bool cbtree_insert(struct CBTree *tree, void *obj) _MUSTCHECK; /** Removed node from tree. * If obj_free_cb is non-NULL, it will be called for the object. * * @returns true if key was found, false otherwise. */ bool cbtree_delete(struct CBTree *tree, const void *key, size_t klen); /** * Lookup a key. * * @returns object pointer if found, NULL ohterwise */ void *cbtree_lookup(struct CBTree *tree, const void *key, size_t klen); /** Walk over tree */ bool cbtree_walk(struct CBTree *tree, cbtree_walker_func cb_func, void *cb_arg); #endif pgbouncer-1.7/lib/usual/mbuf.c0000664000175000017500000000117412511202014013253 00000000000000 /* * Safe and easy access to memory buffer. */ #include bool mbuf_make_room(struct MBuf *buf, unsigned len) { unsigned new_alloc = buf->alloc_len; void *ptr; /* is it a dynamic buffer */ if (buf->reader || buf->fixed) return false; /* maybe there is enough room already */ if (buf->write_pos + len <= buf->alloc_len) return true; if (new_alloc == 0) new_alloc = 128; /* calc new alloc size */ while (new_alloc < buf->write_pos + len) new_alloc *= 2; /* realloc */ ptr = realloc(buf->data, new_alloc); if (!ptr) return false; buf->data = ptr; buf->alloc_len = new_alloc; return true; } pgbouncer-1.7/lib/usual/heap.c0000664000175000017500000000740212511203511013242 00000000000000/* * Binary Heap. * * Copyright (c) 2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include struct Heap { void **data; unsigned allocated; unsigned used; heap_is_better_f is_better; heap_save_pos_f save_pos; CxMem *cx; }; /* * Low-level operations. */ static unsigned get_parent(unsigned i) { return (i - 1) / 2; } static unsigned get_child(unsigned i, unsigned child_nr) { return 2*i + 1 + child_nr; } static bool is_better(struct Heap *h, unsigned i1, unsigned i2) { return h->is_better(h->data[i1], h->data[i2]); } static void set(struct Heap *h, unsigned i, void *ptr) { h->data[i] = ptr; if (h->save_pos) h->save_pos(ptr, i); } static void swap(struct Heap *h, unsigned i1, unsigned i2) { void *tmp = h->data[i1]; set(h, i1, h->data[i2]); set(h, i2, tmp); } static void bubble_up(struct Heap *h, unsigned i) { unsigned p; while (i > 0) { p = get_parent(i); if (!is_better(h, i, p)) break; swap(h, i, p); i = p; } } static void bubble_down(struct Heap *h, unsigned i) { unsigned c = get_child(i, 0); while (c < h->used) { if (c + 1 < h->used) { if (is_better(h, c + 1, c)) c = c + 1; } if (!is_better(h, c, i)) break; swap(h, i, c); i = c; c = get_child(i, 0); } } static void rebalance(struct Heap *h, unsigned pos) { if (pos == 0) { bubble_down(h, pos); } else if (pos == h->used - 1) { bubble_up(h, pos); } else if (is_better(h, pos, get_parent(pos))) { bubble_up(h, pos); } else { bubble_down(h, pos); } } /* * Actual API. */ struct Heap *heap_create(heap_is_better_f is_better_cb, heap_save_pos_f save_pos_cb, CxMem *cx) { struct Heap *h; h = cx_alloc0(cx, sizeof(*h)); if (!h) return NULL; h->save_pos = save_pos_cb; h->is_better = is_better_cb; h->cx = cx; return h; } void heap_destroy(struct Heap *h) { if (h) { cx_free(h->cx, h->data); cx_free(h->cx, h); } } bool heap_reserve(struct Heap *h, unsigned extra) { void *tmp; unsigned newalloc; if (h->used + extra < h->allocated) return true; newalloc = h->allocated * 2; if (newalloc < 32) newalloc = 32; if (newalloc < h->used + extra) newalloc = h->used + extra; tmp = cx_realloc(h->cx, h->data, newalloc * sizeof(void *)); if (!tmp) return false; h->data = tmp; h->allocated = newalloc; return true; } void *heap_top(struct Heap *h) { return (h->used > 0) ? h->data[0] : NULL; } bool heap_push(struct Heap *h, void *ptr) { unsigned pos; if (h->used >= h->allocated) { if (!heap_reserve(h, 1)) return false; } pos = h->used++; set(h, pos, ptr); bubble_up(h, pos); return true; } void *heap_remove(struct Heap *h, unsigned pos) { unsigned last; void *obj; if (pos >= h->used) return NULL; obj = h->data[pos]; last = --h->used; if (pos < last) { set(h, pos, h->data[last]); rebalance(h, pos); } h->data[last] = NULL; return obj; } void *heap_pop(struct Heap *h) { return heap_remove(h, 0); } unsigned heap_size(struct Heap *h) { return h->used; } void *heap_get_obj(struct Heap *h, unsigned pos) { if (pos < h->used) return h->data[pos]; return NULL; } pgbouncer-1.7/lib/usual/socket.h0000664000175000017500000000761012511203511013623 00000000000000/* * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * * Socket compat, few utils. * * Socket headers included: * - win32: * - win32: * - * - * - * - * - * - * - * - */ #ifndef _USUAL_SOCKET_H_ #define _USUAL_SOCKET_H_ #include #ifdef WIN32 #include #include #include #endif #include #ifdef HAVE_SYS_SOCKET_H #include #endif #ifdef HAVE_POLL_H #include #endif #ifdef HAVE_SYS_POLL_H #include #endif #ifdef HAVE_SYS_UIO_H #include #endif #ifdef HAVE_SYS_UN_H #include #endif #ifdef HAVE_NETINET_IN_H #include #endif #ifdef HAVE_NETINET_TCP_H #include #endif #ifdef HAVE_ARPA_INET_H #include #endif #ifndef INADDR_NONE /** Compat: Some systems (Solaris) does not define INADDR_NONE */ #define INADDR_NONE ((unsigned long) -1) #endif /** * Usual socket setup. * * - Disallow SIGPIPE * - Set close-on-exec flag * - Call \ref socket_set_nonblocking() with given flag */ bool socket_setup(int sock, bool non_block); /** * Flip sockets non-blocking flag */ bool socket_set_nonblocking(int sock, bool non_block); /** * Set sockets keepalive flags. * * @param fd TCP socket * @param onoff Whether to set keepalive on or off. * @param keepidle How long the socket must be idle before keepalive packets are sent * @param keepintvl How big period between consecutive keepalive packets. * @param keepcnt How many keepalive packets to send before considering socket dead. */ bool socket_set_keepalive(int fd, int onoff, int keepidle, int keepintvl, int keepcnt); /** * Convert struct sockaddr to stirng. * * Supports: ipv4, ipv5, unix sockets. */ const char *sa2str(const struct sockaddr *sa, char *buf, int buflen); #ifndef HAVE_INET_NTOP #define inet_ntop(a,b,c,d) usual_inet_ntop(a,b,c,d) /** Compat: inet_ntop() */ const char *inet_ntop(int af, const void *src, char *dst, int cnt); #endif #ifndef HAVE_INET_PTON #define inet_pton(a,b,c) usual_inet_pton(a,b,c) /** Compat: inet_pton() */ int inet_pton(int af, const char *src, void *dst); #endif #ifndef HAVE_GETPEEREID #define getpeereid(a,b,c) compat_getpeereid(a,b,c) /** Get user id of UNIX socket peer */ int getpeereid(int fd, uid_t *uid_p, gid_t *gid_p); #endif #define getpeercreds(a,b,c,d) usual_getpeercreds(a,b,c,d) /** Get info of UNIX socket peer */ int getpeercreds(int fd, uid_t *uid_p, gid_t *gid_p, pid_t *pid_p); #if !defined(HAVE_POLL) #define POLLIN (1 << 0) #define POLLOUT (1 << 1) #define POLLHUP (1 << 2) #define POLLPRI (1 << 3) #define POLLNVAL (1 << 4) #define POLLERR (1 << 5) #define poll(a,b,c) compat_poll(a,b,c) struct pollfd { int fd; short events; short revents; }; typedef unsigned long nfds_t; /** Compat: select-based poll() */ int poll(struct pollfd *fds, nfds_t nfds, int timeout_ms); #endif #ifdef WIN32 #define socketpair(a,b,c,d) win32_socketpair(a,b,c,d) /** Compat: socketpair() for win32 */ int socketpair(int d, int typ, int proto, int sv[2]); #endif #endif pgbouncer-1.7/lib/usual/heap.h0000664000175000017500000000542312511202014013245 00000000000000/* * Binary Heap. * * Copyright (c) 2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * Binary heap. * * Binary heap is sort of binary tree held inside array, * with following 2 properties: * - heap property: each node is "better" than it's childs. * - shape property: binary tree is complete, meaning all levels * except the last one are fully filled. * * Instead of "min"- or "max"-heap, this is "best"-heap, * as it operates with user-defined heap_is_better() functions, * which is used to bubble elements on top. */ #ifndef _USUAL_HEAP_H_ #define _USUAL_HEAP_H_ #include /** * Object comparision function. * * Should return true if a needs to reach top before b, * false if not or equal. */ typedef bool (*heap_is_better_f)(const void *a, const void *b); /** * Heap position storage. * * If user wants to delete elements from the middle of heap, * this function should be used to keep track where the element * is located. */ typedef void (*heap_save_pos_f)(void *a, unsigned pos); /** * Heap object. */ struct Heap; /** * Create new heap object. * * @param is_better_cb Callback to decide priority. * @param save_pos_cb Callback to store current index. * @param cx Allocation context. */ struct Heap *heap_create( heap_is_better_f is_better_cb, heap_save_pos_f save_pos_cb, CxMem *cx); /** Release memory allocated by heap */ void heap_destroy(struct Heap *h); /** Put new object into heap */ bool heap_push(struct Heap *h, void *ptr); /** Remove and return topmost object from heap */ void *heap_pop(struct Heap *h); /** Return topmost object in heap */ void *heap_top(struct Heap *h); /** Remove and return any object from heap by index */ void *heap_remove(struct Heap *h, unsigned pos); /** * Reserve room for more elements. * * Returns false if allocation failed. */ bool heap_reserve(struct Heap *h, unsigned extra); /** Return number of objects in heap */ unsigned heap_size(struct Heap *h); /* Return object by index, for testing */ void *heap_get_obj(struct Heap *h, unsigned pos); #endif pgbouncer-1.7/lib/usual/socket_ntop.c0000664000175000017500000001235612511202014014656 00000000000000/* $OpenBSD: inet_ntop.c,v 1.8 2008/12/09 19:38:38 otto Exp $ */ /* Copyright (c) 1996 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #include #include #ifndef HAVE_INET_NTOP #ifndef INADDRSZ #define INADDRSZ 4 #endif #ifndef IN6ADDRSZ #define IN6ADDRSZ 16 #endif #ifndef INT16SZ #define INT16SZ 2 #endif #define u_char uint8_t #define u_int unsigned int /* * WARNING: Don't even consider trying to compile this on a system where * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. */ static const char *inet_ntop4(const u_char *src, char *dst, int size); static const char *inet_ntop6(const u_char *src, char *dst, int size); /* char * * inet_ntop(af, src, dst, size) * convert a network format address to presentation format. * return: * pointer to presentation format address (`dst'), or NULL (see errno). * author: * Paul Vixie, 1996. */ const char * inet_ntop(int af, const void *src, char *dst, int size) { if (size < 0) { errno = ENOSPC; return NULL; } switch (af) { case AF_INET: return (inet_ntop4(src, dst, size)); case AF_INET6: return (inet_ntop6(src, dst, size)); default: errno = EAFNOSUPPORT; return (NULL); } /* NOTREACHED */ } /* const char * * inet_ntop4(src, dst, size) * format an IPv4 address, more or less like inet_ntoa() * return: * `dst' (as a const) * notes: * (1) uses no statics * (2) takes a u_char* not an in_addr as input * author: * Paul Vixie, 1996. */ static const char * inet_ntop4(const u_char *src, char *dst, int size) { static const char fmt[] = "%u.%u.%u.%u"; char tmp[sizeof "255.255.255.255"]; int l; l = snprintf(tmp, sizeof(tmp), fmt, src[0], src[1], src[2], src[3]); if (l <= 0 || l >= size) { errno = ENOSPC; return (NULL); } strlcpy(dst, tmp, size); return (dst); } /* const char * * inet_ntop6(src, dst, size) * convert IPv6 binary address into presentation (printable) format * author: * Paul Vixie, 1996. */ static const char * inet_ntop6(const u_char *src, char *dst, int size) { /* * Note that int32_t and int16_t need only be "at least" large enough * to contain a value of the specified size. On some systems, like * Crays, there is no such thing as an integer variable with 16 bits. * Keep this in mind if you think this function should have been coded * to use pointer overlays. All the world's not a VAX. */ char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"]; char *tp, *ep; struct { int base, len; } best, cur; u_int words[IN6ADDRSZ / INT16SZ]; int i; int advance; /* * Preprocess: * Copy the input (bytewise) array into a wordwise array. * Find the longest run of 0x00's in src[] for :: shorthanding. */ memset(words, '\0', sizeof words); for (i = 0; i < IN6ADDRSZ; i++) words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); best.base = best.len = -1; cur.base = cur.len = -1; for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++) { if (words[i] == 0) { if (cur.base == -1) cur.base = i, cur.len = 1; else cur.len++; } else { if (cur.base != -1) { if (best.base == -1 || cur.len > best.len) best = cur; cur.base = -1; } } } if (cur.base != -1) { if (best.base == -1 || cur.len > best.len) best = cur; } if (best.base != -1 && best.len < 2) best.base = -1; /* * Format the result. */ tp = tmp; ep = tmp + sizeof(tmp); for (i = 0; i < (IN6ADDRSZ / INT16SZ) && tp < ep; i++) { /* Are we inside the best run of 0x00's? */ if (best.base != -1 && i >= best.base && i < (best.base + best.len)) { if (i == best.base) { if (tp + 1 >= ep) return (NULL); *tp++ = ':'; } continue; } /* Are we following an initial run of 0x00s or any real hex? */ if (i != 0) { if (tp + 1 >= ep) return (NULL); *tp++ = ':'; } /* Is this address an encapsulated IPv4? */ if (i == 6 && best.base == 0 && (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) { if (!inet_ntop4(src+12, tp, (size_t)(ep - tp))) return (NULL); tp += strlen(tp); break; } advance = snprintf(tp, ep - tp, "%x", words[i]); if (advance <= 0 || advance >= ep - tp) return (NULL); tp += advance; } /* Was it a trailing run of 0x00's? */ if (best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ)) { if (tp + 1 >= ep) return (NULL); *tp++ = ':'; } if (tp + 1 >= ep) return (NULL); *tp++ = '\0'; /* * Check for overflow, copy, and we're done. */ if ((tp - tmp) > size) { errno = ENOSPC; return (NULL); } strlcpy(dst, tmp, size); return (dst); } #endif pgbouncer-1.7/lib/usual/cxextra.h0000664000175000017500000000326612511203511014014 00000000000000/* * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Extra allocators for cxalloc. */ #ifndef _USUAL_CXEXTRA_H_ #define _USUAL_CXEXTRA_H_ #include /** Allocator that exits on error. .ctx should be pointer to actual allocator */ extern const struct CxOps cx_nofail_ops; /** nofail for libc */ extern CxMem cx_libc_nofail; /** * Creates allocator that pools all memory together, * without keeping track of single objects, to be * freed all together in one shot. * * realloc(), free() are partially supported for the last * objec only. */ CxMem *cx_new_pool(CxMem *parent, size_t initial_size, unsigned int align); CxMem *cx_new_pool_from_area(CxMem *parent, void *buf, size_t size, bool allow_free, unsigned int align); /** * Creates allocator that remebers all allocations done * under it and allows all of it to be freed together. * * Supports hierarchical trees. */ CxMem *cx_new_tree(CxMem *parent); #endif pgbouncer-1.7/lib/usual/statlist.h0000664000175000017500000001011112511203511014170 00000000000000/* * Wrapper for list.h that keeps track of number of items. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Circular list that keep track of stats about the list. * * Currenly only count of abjects currently in list * is kept track of. The plan was to track more, * like max, but it was not useful enough. */ #ifndef _USUAL_STATLIST_H_ #define _USUAL_STATLIST_H_ #include /** * Header structure for StatList. */ struct StatList { /** Actual list head */ struct List head; /** Count of objects currently in list */ int cur_count; #ifdef LIST_DEBUG /** List name */ const char *name; #endif }; /** Define and initialize StatList head */ #ifdef LIST_DEBUG #define STATLIST(var) struct StatList var = { {&var.head, &var.head}, 0, #var } #else #define STATLIST(var) struct StatList var = { {&var.head, &var.head}, 0 } #endif /** Add to the start of the list */ static inline void statlist_prepend(struct StatList *list, struct List *item) { list_prepend(&list->head, item); list->cur_count++; } /** Add to the end of the list */ static inline void statlist_append(struct StatList *list, struct List *item) { list_append(&list->head, item); list->cur_count++; } /** Remove element from the list */ static inline void statlist_remove(struct StatList *list, struct List *item) { list_del(item); list->cur_count--; /* Assert(list->cur_count >= 0); */ } /** Initialize StatList head */ static inline void statlist_init(struct StatList *list, const char *name) { list_init(&list->head); list->cur_count = 0; #ifdef LIST_DEBUG list->name = name; #endif } /** return number of elements currently in list */ static inline int statlist_count(const struct StatList *list) { /* Assert(list->cur_count > 0 || list_empty(&list->head)); */ return list->cur_count; } /** remove and return first element */ static inline struct List *statlist_pop(struct StatList *list) { struct List *item = list_pop(&list->head); if (item) list->cur_count--; /* Assert(list->cur_count >= 0); */ return item; } /** Return first element */ static inline struct List *statlist_first(const struct StatList *list) { return list_first(&list->head); } /** Return last element */ static inline struct List *statlist_last(const struct StatList *list) { return list_last(&list->head); } /** Is list empty */ static inline bool statlist_empty(const struct StatList *list) { return list_empty(&list->head); } /** Loop over list */ #define statlist_for_each(item, list) list_for_each(item, &((list)->head)) /** Loop over list backwards */ #define statlist_for_each_reverse(item, list) list_for_each_reverse(item, &((list)->head)) /** Loop over list safely, so that elements can be removed during */ #define statlist_for_each_safe(item, list, tmp) list_for_each_safe(item, &((list)->head), tmp) /** Loop over list backwards safely, so that elements can be removed during */ #define statlist_for_each_reverse_safe(item, list, tmp) list_for_each_reverse_safe(item, &((list)->head), tmp) /** Put intem before another */ static inline void statlist_put_before(struct StatList *list, struct List *item, struct List *pos) { list_append(pos, item); list->cur_count++; } /** Put item after another */ static inline void statlist_put_after(struct StatList *list, struct List *item, struct List *pos) { list_prepend(pos, item); list->cur_count++; } #endif /* __LIST_H_ */ pgbouncer-1.7/lib/usual/cxextra.c0000664000175000017500000001635612511203511014013 00000000000000 /* * Extra allocators */ #include #include #include #include /* * Tools for allocators. */ static inline void *p_move(const void *p, int ofs) { return (char *)p + ofs; } /* * sample exit-on-failure wrapper */ static void *nofail_alloc(void *next, size_t len) { void *p = cx_alloc(next, len); if (!p) exit(1); return p; } static void *nofail_realloc(void *next, void *ptr, size_t len) { void *p = cx_realloc(next, ptr, len); if (!p) exit(1); return p; } static void nofail_free(void *next, const void *ptr) { cx_free(next, ptr); } static void nofail_destroy(void *next) { cx_destroy(next); } const struct CxOps cx_nofail_ops = { nofail_alloc, nofail_realloc, nofail_free, nofail_destroy, }; const struct CxMem cx_libc_nofail = { &cx_nofail_ops, (void*)&cx_libc_allocator, }; /* * Append-only pool. */ struct CxPoolSeg { struct CxPoolSeg *prev; unsigned char *seg_start; unsigned char *seg_pos; unsigned char *seg_end; }; struct CxPool { struct CxMem this; const struct CxMem *parent; struct CxPoolSeg *last; unsigned char *last_ptr; unsigned int align; bool allow_free_first; struct CxPoolSeg first_seg; }; #define POOL_HDR ALIGN(sizeof(struct CxPoolSeg)) static struct CxPoolSeg *new_seg(struct CxPool *pool, size_t nsize) { struct CxPoolSeg *seg; unsigned char *ptr; size_t alloc = POOL_HDR + nsize; seg = cx_alloc(pool->parent, alloc); if (seg == NULL) return NULL; ptr = (unsigned char *)seg; seg->seg_start = (void *)CUSTOM_ALIGN(ptr + POOL_HDR, pool->align); seg->seg_pos = seg->seg_start; seg->seg_end = (unsigned char *)seg + alloc; seg->prev = pool->last; pool->last = seg; pool->last_ptr = NULL; return seg; } static void *pool_alloc(void *ctx, size_t size) { struct CxPool *pool = ctx; struct CxPoolSeg *seg = pool->last; void *ptr; unsigned nsize; size = CUSTOM_ALIGN(size, pool->align); if (seg && seg->seg_pos + size <= seg->seg_end) { ptr = seg->seg_pos; seg->seg_pos += size; pool->last_ptr = ptr; return ptr; } else { nsize = seg ? (2 * (seg->seg_end - seg->seg_start)) : 512; while (nsize < size) nsize *= 2; seg = new_seg(pool, nsize); if (!seg) return NULL; ptr = seg->seg_pos; seg->seg_pos += size; pool->last_ptr = ptr; return ptr; } } /* free only last item */ static void pool_free(void *ctx, const void *ptr) { struct CxPool *pool = ctx; struct CxPoolSeg *cur = pool->last; if (pool->last_ptr != ptr) return; cur->seg_pos = (void *)ptr; pool->last_ptr = NULL; } static size_t pool_guess_old_len(struct CxPool *pool, unsigned char *ptr) { struct CxPoolSeg *seg = pool->last; unsigned char *cstart; while (seg) { cstart = (void *)CUSTOM_ALIGN((seg + 1), pool->align); if (ptr >= cstart && ptr < seg->seg_pos) return seg->seg_pos - ptr; seg = seg->prev; } return 0; } /* realloc only last item properly, otherwise do new alloc */ static void *pool_realloc(void *ctx, void *ptr, size_t len) { struct CxPool *pool = ctx; struct CxPoolSeg *seg = pool->last; unsigned char *p = ptr; size_t olen; if (pool->last_ptr != ptr) { olen = pool_guess_old_len(pool, ptr); p = pool_alloc(ctx, len); if (!p) return NULL; if (olen > len) olen = len; memcpy(p, ptr, olen); return p; } olen = seg->seg_pos - p; if (seg->seg_pos - olen + len <= seg->seg_end) { seg->seg_pos = p + len; return p; } else { p = pool_alloc(ctx, len); if (!p) return NULL; memcpy(p, ptr, olen); return p; } } static void pool_destroy(void *ctx) { struct CxPool *pool = ctx; struct CxPoolSeg *cur, *prev; if (!pool) return; for (cur = pool->last; cur; ) { prev = cur->prev; if (!prev) break; cx_free(pool->parent, cur); cur = prev; } if (pool->allow_free_first) cx_free(pool->parent, pool); } static const struct CxOps pool_ops = { pool_alloc, pool_realloc, pool_free, pool_destroy, }; /* * public functions */ CxMem *cx_new_pool_from_area(CxMem *parent, void *buf, size_t size, bool allow_free, unsigned int align) { struct CxPool *head; if (size < sizeof(struct CxPool)) return NULL; if (align == 0) align = 8; else if (!is_power_of_2(align)) return NULL; head = buf; memset(head, 0, sizeof(struct CxPool)); head->parent = parent; head->this.ops = &pool_ops; head->this.ctx = head; head->last = &head->first_seg; head->allow_free_first = allow_free; head->align = align; head->first_seg.seg_start = (void *)CUSTOM_ALIGN(head + 1, align); head->first_seg.seg_pos = head->first_seg.seg_start; head->first_seg.seg_end = (unsigned char *)head + size; return &head->this; } CxMem *cx_new_pool(CxMem *parent, size_t initial_size, unsigned int align) { void *area; size_t size; if (initial_size < 1024) initial_size = 1024; size = sizeof(struct CxPool) + initial_size; area = cx_alloc(parent, size); if (!area) return NULL; return cx_new_pool_from_area(parent, area, size, true, align); } /* * tree alloc */ #define TREE_HDR (int)(sizeof(struct CxTreeItem)) struct CxTree { struct CxMem this; CxMem *real; struct List alloc_list; struct List subtree_node; struct List subtree_list; }; /* header for each allocation */ struct CxTreeItem { struct List node; }; static void *tree_alloc(void *ctx, size_t len) { struct CxTree *tree = ctx; struct CxTreeItem *item; item = cx_alloc(tree->real, TREE_HDR + len); if (!item) return NULL; list_init(&item->node); list_append(&tree->alloc_list, &item->node); return p_move(item, TREE_HDR); } static void *tree_realloc(void *ctx, void *ptr, size_t len) { struct CxTree *t = ctx; struct CxTreeItem *item, *item2; item = p_move(ptr, -TREE_HDR); list_del(&item->node); item2 = cx_realloc(t->real, item, TREE_HDR + len); if (item2) { list_append(&t->alloc_list, &item2->node); return p_move(item2, TREE_HDR); } else { list_append(&t->alloc_list, &item->node); return NULL; } } static void tree_free(void *ctx, const void *ptr) { struct CxTree *t = ctx; struct CxTreeItem *item; item = p_move(ptr, -TREE_HDR); list_del(&item->node); cx_free(t->real, item); } static void tree_destroy(void *ctx) { struct CxTree *tree = ctx, *sub; struct CxTreeItem *item; struct List *el, *tmp; /* unregister from parent */ list_del(&tree->subtree_node); /* free elements */ list_for_each_safe(el, &tree->alloc_list, tmp) { list_del(el); item = container_of(el, struct CxTreeItem, node); cx_free(tree->real, item); } /* free subtrees */ list_for_each_safe(el, &tree->subtree_list, tmp) { sub = container_of(el, struct CxTree, subtree_node); tree_destroy(sub); } /* free base struct */ cx_free(tree->real, tree); } static const struct CxOps tree_ops = { tree_alloc, tree_realloc, tree_free, tree_destroy, }; CxMem *cx_new_tree(CxMem *cx) { struct CxTree *t, *parent = NULL; CxMem *real = cx; /* * Try to allocate from real allocator. Otherwise allocations * will have double headers. */ if (cx->ops == &tree_ops) { parent = cx->ctx; real = parent->real; } /* initialize */ t = cx_alloc(real, sizeof(*t)); if (!t) return NULL; t->real = real; t->this.ops = &tree_ops; t->this.ctx = t; list_init(&t->alloc_list); list_init(&t->subtree_node); list_init(&t->subtree_list); /* register at parent */ if (parent) list_append(&parent->subtree_list, &t->subtree_node); return &t->this; } pgbouncer-1.7/lib/usual/getopt.c0000664000175000017500000003245212511202014013627 00000000000000/* $OpenBSD: getopt_long.c,v 1.24 2010/07/22 19:31:53 blambert Exp $ */ /* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */ /* * Copyright (c) 2002 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Sponsored in part by the Defense Advanced Research Projects * Agency (DARPA) and Air Force Research Laboratory, Air Force * Materiel Command, USAF, under agreement number F39502-99-1-0512. */ /*- * Copyright (c) 2000 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Dieter Baron and Thomas Klausner. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #ifdef NEED_USUAL_GETOPT #include #include char *optarg; /* argument associated with option */ int opterr = 1; /* if error message should be printed */ int optind = 1; /* index into parent argv vector */ int optopt = '?'; /* character checked for validity */ #define PRINT_ERROR ((opterr) && (*options != ':')) #define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ #define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */ #define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */ /* return values */ #define BADCH (int)'?' #define BADARG ((*options == ':') ? (int)':' : (int)'?') #define INORDER (int)1 #define EMSG "" static char *place = EMSG; /* option letter processing */ /* XXX: set optreset to 1 rather than these two */ static int nonopt_start = -1; /* first non option argument (for permute) */ static int nonopt_end = -1; /* first option after non options (for permute) */ /* Error messages */ static const char recargchar[] = "option requires an argument -- %c"; static const char recargstring[] = "option requires an argument -- %s"; static const char ambig[] = "ambiguous option -- %.*s"; static const char noarg[] = "option doesn't take an argument -- %.*s"; static const char illoptchar[] = "unknown option -- %c"; static const char illoptstring[] = "unknown option -- %s"; /* * Compute the greatest common divisor of a and b. */ static int gcd(int a, int b) { int c; c = a % b; while (c != 0) { a = b; b = c; c = a % b; } return (b); } /* * Exchange the block from nonopt_start to nonopt_end with the block * from nonopt_end to opt_end (keeping the same order of arguments * in each block). */ static void permute_args(int panonopt_start, int panonopt_end, int opt_end, char * const *nargv) { int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; char *swap; /* * compute lengths of blocks and number and size of cycles */ nnonopts = panonopt_end - panonopt_start; nopts = opt_end - panonopt_end; ncycle = gcd(nnonopts, nopts); cyclelen = (opt_end - panonopt_start) / ncycle; for (i = 0; i < ncycle; i++) { cstart = panonopt_end+i; pos = cstart; for (j = 0; j < cyclelen; j++) { if (pos >= panonopt_end) pos -= nnonopts; else pos += nopts; swap = nargv[pos]; /* LINTED const cast */ ((char **) nargv)[pos] = nargv[cstart]; /* LINTED const cast */ ((char **)nargv)[cstart] = swap; } } } /* * parse_long_options -- * Parse long options in argc/argv argument vector. * Returns -1 if short_too is set and the option does not match long_options. */ static int parse_long_options(char * const *nargv, const char *options, const struct option *long_options, int *idx, int short_too) { char *current_argv, *has_equal; size_t current_argv_len; int i, match; current_argv = place; match = -1; optind++; if ((has_equal = strchr(current_argv, '=')) != NULL) { /* argument found (--option=arg) */ current_argv_len = has_equal - current_argv; has_equal++; } else current_argv_len = strlen(current_argv); for (i = 0; long_options[i].name; i++) { /* find matching long option */ if (strncmp(current_argv, long_options[i].name, current_argv_len)) continue; if (strlen(long_options[i].name) == current_argv_len) { /* exact match */ match = i; break; } /* * If this is a known short option, don't allow * a partial match of a single character. */ if (short_too && current_argv_len == 1) continue; if (match == -1) /* partial match */ match = i; else { /* ambiguous abbreviation */ if (PRINT_ERROR) warnx(ambig, (int)current_argv_len, current_argv); optopt = 0; return (BADCH); } } if (match != -1) { /* option found */ if (long_options[match].has_arg == no_argument && has_equal) { if (PRINT_ERROR) warnx(noarg, (int)current_argv_len, current_argv); /* * XXX: GNU sets optopt to val regardless of flag */ if (long_options[match].flag == NULL) optopt = long_options[match].val; else optopt = 0; return (BADARG); } if (long_options[match].has_arg == required_argument || long_options[match].has_arg == optional_argument) { if (has_equal) optarg = has_equal; else if (long_options[match].has_arg == required_argument) { /* * optional argument doesn't use next nargv */ optarg = nargv[optind++]; } } if ((long_options[match].has_arg == required_argument) && (optarg == NULL)) { /* * Missing argument; leading ':' indicates no error * should be generated. */ if (PRINT_ERROR) warnx(recargstring, current_argv); /* * XXX: GNU sets optopt to val regardless of flag */ if (long_options[match].flag == NULL) optopt = long_options[match].val; else optopt = 0; --optind; return (BADARG); } } else { /* unknown option */ if (short_too) { --optind; return (-1); } if (PRINT_ERROR) warnx(illoptstring, current_argv); optopt = 0; return (BADCH); } if (idx) *idx = match; if (long_options[match].flag) { *long_options[match].flag = long_options[match].val; return (0); } else return (long_options[match].val); } /* * getopt_internal -- * Parse argc/argv argument vector. Called by user level routines. */ static int getopt_internal(int nargc, char * const *nargv, const char *options, const struct option *long_options, int *idx, int flags) { char *oli; /* option letter list index */ int optchar, short_too; static int posixly_correct = -1; int optreset = 0; if (options == NULL) return (-1); /* * Disable GNU extensions if POSIXLY_CORRECT is set or options * string begins with a '+'. */ if (posixly_correct == -1) posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); if (posixly_correct || *options == '+') flags &= ~FLAG_PERMUTE; else if (*options == '-') flags |= FLAG_ALLARGS; if (*options == '+' || *options == '-') options++; /* * reset if requested */ if (optind == 0) optind = optreset = 1; optarg = NULL; if (optreset) nonopt_start = nonopt_end = -1; start: if (optreset || !*place) { /* update scanning pointer */ optreset = 0; if (optind >= nargc) { /* end of argument vector */ place = EMSG; if (nonopt_end != -1) { /* do permutation, if we have to */ permute_args(nonopt_start, nonopt_end, optind, nargv); optind -= nonopt_end - nonopt_start; } else if (nonopt_start != -1) { /* * If we skipped non-options, set optind * to the first of them. */ optind = nonopt_start; } nonopt_start = nonopt_end = -1; return (-1); } if (*(place = nargv[optind]) != '-' || (place[1] == '\0' && strchr(options, '-') == NULL)) { place = EMSG; /* found non-option */ if (flags & FLAG_ALLARGS) { /* * GNU extension: * return non-option as argument to option 1 */ optarg = nargv[optind++]; return (INORDER); } if (!(flags & FLAG_PERMUTE)) { /* * If no permutation wanted, stop parsing * at first non-option. */ return (-1); } /* do permutation */ if (nonopt_start == -1) nonopt_start = optind; else if (nonopt_end != -1) { permute_args(nonopt_start, nonopt_end, optind, nargv); nonopt_start = optind - (nonopt_end - nonopt_start); nonopt_end = -1; } optind++; /* process next argument */ goto start; } if (nonopt_start != -1 && nonopt_end == -1) nonopt_end = optind; /* * If we have "-" do nothing, if "--" we are done. */ if (place[1] != '\0' && *++place == '-' && place[1] == '\0') { optind++; place = EMSG; /* * We found an option (--), so if we skipped * non-options, we have to permute. */ if (nonopt_end != -1) { permute_args(nonopt_start, nonopt_end, optind, nargv); optind -= nonopt_end - nonopt_start; } nonopt_start = nonopt_end = -1; return (-1); } } /* * Check long options if: * 1) we were passed some * 2) the arg is not just "-" * 3) either the arg starts with -- we are getopt_long_only() */ if (long_options != NULL && place != nargv[optind] && (*place == '-' || (flags & FLAG_LONGONLY))) { short_too = 0; if (*place == '-') place++; /* --foo long option */ else if (*place != ':' && strchr(options, *place) != NULL) short_too = 1; /* could be short option too */ optchar = parse_long_options(nargv, options, long_options, idx, short_too); if (optchar != -1) { place = EMSG; return (optchar); } } if ((optchar = (int)*place++) == (int)':' || (optchar == (int)'-' && *place != '\0') || (oli = strchr(options, optchar)) == NULL) { /* * If the user specified "-" and '-' isn't listed in * options, return -1 (non-option) as per POSIX. * Otherwise, it is an unknown option character (or ':'). */ if (optchar == (int)'-' && *place == '\0') return (-1); if (!*place) ++optind; if (PRINT_ERROR) warnx(illoptchar, optchar); optopt = optchar; return (BADCH); } if (long_options != NULL && optchar == 'W' && oli[1] == ';') { /* -W long-option */ if (*place) /* no space */ /* NOTHING */; else if (++optind >= nargc) { /* no arg */ place = EMSG; if (PRINT_ERROR) warnx(recargchar, optchar); optopt = optchar; return (BADARG); } else /* white space */ place = nargv[optind]; optchar = parse_long_options(nargv, options, long_options, idx, 0); place = EMSG; return (optchar); } if (*++oli != ':') { /* doesn't take argument */ if (!*place) ++optind; } else { /* takes (optional) argument */ optarg = NULL; if (*place) /* no white space */ optarg = place; else if (oli[1] != ':') { /* arg not optional */ if (++optind >= nargc) { /* no arg */ place = EMSG; if (PRINT_ERROR) warnx(recargchar, optchar); optopt = optchar; return (BADARG); } else optarg = nargv[optind]; } place = EMSG; ++optind; } /* dump back option letter */ return (optchar); } /* * getopt -- * Parse argc/argv argument vector. */ int getopt(int nargc, char *nargv[], const char *options) { return getopt_internal(nargc, nargv, options, NULL, NULL, FLAG_PERMUTE); } /* * getopt_long -- * Parse argc/argv argument vector. */ int getopt_long(int nargc, char *nargv[], const char *options, const struct option *long_options, int *idx) { return (getopt_internal(nargc, nargv, options, long_options, idx, FLAG_PERMUTE)); } /* * getopt_long_only -- * Parse argc/argv argument vector. */ int getopt_long_only(int nargc, char *nargv[], const char *options, const struct option *long_options, int *idx) { return (getopt_internal(nargc, nargv, options, long_options, idx, FLAG_PERMUTE|FLAG_LONGONLY)); } #endif /* NEED_USUAL_GETOPT */ pgbouncer-1.7/lib/usual/cxalloc.c0000664000175000017500000000627312560223470013771 00000000000000/* * libusual - Utility library for C * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include /* * Utility routines for cx_* API. */ void *cx_alloc(CxMem *cx, size_t len) { if (!len) return NULL; if (!cx) cx = USUAL_ALLOC; return cx->ops->c_alloc(cx->ctx, len); } void *cx_realloc(CxMem *cx, void *ptr, size_t len) { if (!cx) cx = USUAL_ALLOC; if (!ptr) return cx_alloc(cx, len); if (!len) { cx_free(cx, ptr); return NULL; } return cx->ops->c_realloc(cx->ctx, ptr, len); } void cx_free(CxMem *cx, const void *ptr) { if (!cx) cx = USUAL_ALLOC; if (ptr) cx->ops->c_free(cx->ctx, ptr); } void cx_destroy(CxMem *cx) { if (!cx) return; if (!cx->ops->c_destroy) abort(); cx->ops->c_destroy(cx->ctx); } void *cx_alloc0(CxMem *cx, size_t len) { void *p = cx_alloc(cx, len); if (p) memset(p, 0, len); return p; } void *cx_memdup(CxMem *cx, const void *src, size_t len) { void *p = cx_alloc(cx, len); if (p) memcpy(p, src, len); return p; } void *cx_strdup(CxMem *cx, const char *s) { return cx_memdup(cx, s, strlen(s) + 1); } char *cx_sprintf(CxMem *cx, const char *fmt, ...) { char *res; va_list ap; va_start(ap, fmt); res = cx_vsprintf(cx, fmt, ap); va_end(ap); return res; } char *cx_vsprintf(CxMem *cx, const char *fmt, va_list ap) { char *res; cx_vasprintf(cx, &res, fmt, ap); return res; } int cx_asprintf(CxMem *cx, char **dst_p, const char *fmt, ...) { int res; va_list ap; va_start(ap, fmt); res = cx_vasprintf(cx, dst_p, fmt, ap); va_end(ap); return res; } int cx_vasprintf(CxMem *cx, char **dst_p, const char *fmt, va_list ap) { char buf[128], *dst; int res, res2; *dst_p = NULL; res = vsnprintf(buf, sizeof buf, fmt, ap); if (res < 0) return -1; dst = cx_alloc(cx, res + 1); if (!dst) return -1; if ((size_t)res < sizeof buf) { memcpy(dst, buf, res+1); } else { res2 = vsnprintf(dst, res+1, fmt, ap); if (res2 != res) { cx_free(cx, dst); return -1; } } *dst_p = dst; return res; } /* * Base allocator that uses libc routines. */ static void *libc_alloc(void *ctx, size_t len) { return malloc(len); } static void *libc_realloc(void *ctx, void *ptr, size_t len) { return realloc(ptr, len); } static void libc_free(void *ctx, const void *ptr) { free(ptr); } static const struct CxOps libc_alloc_ops = { libc_alloc, libc_realloc, libc_free, }; const struct CxMem cx_libc_allocator = { &libc_alloc_ops, NULL, }; pgbouncer-1.7/lib/usual/strpool.c0000664000175000017500000000557312511203511014036 00000000000000/* * Pool for shared strings. * * Copyright (c) 2010 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include /* * Put all strings into cbtree. */ struct StrPool { CxMem *ca; struct CBTree *tree; int count; }; /* pass key info to cbtree */ static size_t get_key(void *ctx, void *obj, const void **dst_p) { struct PStr *s = obj; *dst_p = s->str; return s->len; } /* free PStr obj */ static bool free_str(void *arg, void *obj) { struct PStr *p = obj; struct StrPool *sp = p->pool; memset(p, 0, offsetof(struct PStr, str) + 1); cx_free(sp->ca, obj); return true; } /* create main structure */ struct StrPool *strpool_create(CxMem *ca) { struct StrPool *sp; sp = cx_alloc(ca, sizeof(*sp)); if (!sp) return NULL; sp->count = 0; sp->ca = ca; sp->tree = cbtree_create(get_key, NULL, NULL, ca); if (!sp->tree) { cx_free(ca, sp); return NULL; } return sp; } /* free main structure */ void strpool_free(struct StrPool *sp) { if (sp) { cbtree_walk(sp->tree, free_str, sp); cbtree_destroy(sp->tree); cx_free(sp->ca, sp); } } /* return total count of strings in pool */ int strpool_total(struct StrPool *sp) { return sp->count; } /* get new reference to str */ struct PStr *strpool_get(struct StrPool *sp, const char *str, ssize_t len) { struct PStr *cstr; bool ok; if (len < 0) len = strlen(str); /* search */ cstr = cbtree_lookup(sp->tree, str, len); if (cstr) { cstr->refcnt++; return cstr; } /* create */ cstr = cx_alloc(sp->ca, sizeof(*cstr) + len + 1); if (!cstr) return NULL; cstr->pool = sp; cstr->refcnt = 1; cstr->len = len; memcpy(cstr->str, str, len + 1); /* insert */ ok = cbtree_insert(sp->tree, cstr); if (!ok) { cx_free(sp->ca, cstr); return NULL; } sp->count++; return cstr; } /* add reference */ void strpool_incref(struct PStr *s) { if (s) s->refcnt++; } /* drop reference, free if none left */ void strpool_decref(struct PStr *s) { struct StrPool *sp; if (!s) return; Assert(s->refcnt > 0); s->refcnt--; if (s->refcnt > 0) return; /* remove */ sp = s->pool; sp->count--; cbtree_delete(sp->tree, s->str, s->len); free_str(NULL, s); } pgbouncer-1.7/lib/usual/event.c0000664000175000017500000004066512630313715013470 00000000000000/* * event.c - libevent compatible event loop. * * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Small poll()-based async event loop, API-compatible with libevent. * * For sitations where full libevent is not necessary. */ #include #ifndef HAVE_LIBEVENT #include #include #include #include #include #ifndef MSG_NOSIGNAL #define MSG_NOSIGNAL 0 #endif /* max number of signals we care about */ #define MAX_SIGNAL 32 /* if tv_sec is larger, it's absolute timeout */ #define MAX_REL_TIMEOUT (30*24*60*60) /* if no nearby timeouts, max time to sleep (usecs) */ #define MAX_SLEEP (5*USEC) /* extra event flag to track if event is added */ #define EV_ACTIVE 0x80 struct event_base { /* pending timeouts */ struct Heap *timeout_heap; /* fd events */ struct StatList fd_list; /* pollfd <-> event mapping */ struct event **pfd_event; struct pollfd *pfd_list; int pfd_size; /* signal handling */ struct List sig_node; unsigned int sig_seen[MAX_SIGNAL]; struct List sig_waiters[MAX_SIGNAL]; int sig_send, sig_recv; struct event sig_ev; /* exit loop ASAP */ bool loop_break; /* finish current loop and exit */ bool loop_exit; /* cache if refreshed after each poll() */ usec_t cached_time; }; /* default event base */ static struct event_base *current_base; /* global signal data */ static volatile unsigned int sig_count[MAX_SIGNAL]; static bool signal_set_up[MAX_SIGNAL]; static struct sigaction old_handler[MAX_SIGNAL]; static LIST(sig_base_list); /* internal signal functions */ static bool sig_init(struct event_base *base, int sig); static void sig_close(struct event_base *base); /* * Debugging. */ #ifdef CASSERT #include #include static void base_dbg(struct event_base *base, const char *s, ...) { va_list ap; char buf[1024]; va_start(ap, s); vsnprintf(buf, sizeof(buf), s, ap); va_end(ap); log_noise("event base=%p: fdlist=%u timeouts=%d pfds=%d: %s", base, statlist_count(&base->fd_list), heap_size(base->timeout_heap), base->pfd_size, buf); } static void ev_dbg(struct event *ev, const char *s, ...) { va_list ap; char buf[1024], tval[128]; const char *typ = (ev->flags & EV_SIGNAL) ? "sig" : "fd"; va_start(ap, s); vsnprintf(buf, sizeof(buf), s, ap); va_end(ap); log_noise("event %s %d (flags=%s%s%s%s%s) [%s]: %s", typ, ev->fd, (ev->flags & EV_ACTIVE) ? "A" : "", (ev->flags & EV_PERSIST) ? "P" : "", (ev->flags & EV_TIMEOUT) ? "T" : "", (ev->flags & EV_READ) ? "R" : "", (ev->flags & EV_WRITE) ? "W" : "", (ev->flags & EV_TIMEOUT) ? format_time_ms(ev->timeout_val, tval, sizeof(tval)) : "-", buf); } #else #define base_dbg(b, ...) #define ev_dbg(b, ...) #endif /* * Helper functions. */ /* per-base time cache */ static usec_t get_base_time(struct event_base *base) { if (!base->cached_time) base->cached_time = get_time_usec(); return base->cached_time; } /* reset cached time */ static void reset_base_time(struct event_base *base) { base->cached_time = 0; } /* convert user tv to absolute tv */ static usec_t convert_timeout(struct event_base *base, struct timeval *tv) { usec_t val = tv->tv_sec * USEC + tv->tv_usec; if (tv->tv_sec < MAX_REL_TIMEOUT) val += get_base_time(base); return val; } static bool ev_is_better(const void *a, const void *b) { const struct event *ev1 = a, *ev2 = b; return ev1->timeout_val < ev2->timeout_val; } static void ev_save_pos(void *obj, unsigned pos) { struct event *ev = obj; ev->timeout_idx = pos; } /* enlarge pollfd array if needed */ static bool make_room(struct event_base *base, int need) { int total; void *tmp1; void *tmp2; if (need < base->pfd_size) return true; total = base->pfd_size * 2; if (total < 8) total = 8; while (total < need) total *= 2; tmp1 = realloc(base->pfd_list, total * sizeof(struct pollfd)); if (!tmp1) return false; base->pfd_list = tmp1; tmp2 = realloc(base->pfd_event, total * sizeof(struct event *)); if (!tmp2) return false; base->pfd_event = tmp2; base->pfd_size = total; return true; } /* * Single base functions. */ struct event_base *event_init(void) { struct event_base *base = event_base_new(); if (!current_base) current_base = base; return base; } int event_loop(int loop_flags) { return event_base_loop(current_base, loop_flags); } int event_loopbreak(void) { return event_base_loopbreak(current_base); } void event_set(struct event *ev, int fd, short flags, uevent_cb_f cb, void *arg) { event_assign(ev, current_base, fd, flags, cb, arg); } int event_once(int fd, short flags, uevent_cb_f cb_func, void *cb_arg, struct timeval *timeout) { return event_base_once(current_base, fd, flags, cb_func, cb_arg, timeout); } int event_loopexit(struct timeval *timeout) { return event_base_loopexit(current_base, timeout); } /* * Event base initialization. */ struct event_base *event_base_new(void) { struct event_base *base; int i; base = calloc(1, sizeof(*base)); if (!base) return NULL; /* initialize timeout and fd areas */ base->timeout_heap = heap_create(ev_is_better, ev_save_pos, NULL); if (!base->timeout_heap) { free(base); return NULL; } statlist_init(&base->fd_list, "fd_list"); /* initialize signal areas */ for (i = 0; i < MAX_SIGNAL; i++) list_init(&base->sig_waiters[i]); list_init(&base->sig_node); base->sig_send = base->sig_recv = -1; /* allocate pollfds */ if (!make_room(base, 8)) { event_base_free(base); return NULL; } return base; } void event_base_free(struct event_base *base) { if (!base) { if (!current_base) return; base = current_base; } if (base == current_base) current_base = NULL; heap_destroy(base->timeout_heap); free(base->pfd_event); free(base->pfd_list); sig_close(base); free(base); } /* set flag to exit loop ASAP */ int event_base_loopbreak(struct event_base *base) { base->loop_break = true; return 0; } /* * Multi-base functions. */ /* fill event structure */ void event_assign(struct event *ev, struct event_base *base, int fd, short flags, uevent_cb_f cb, void *arg) { Assert(base); Assert((ev->flags & EV_ACTIVE) == 0); if (base == NULL) base = current_base; ev->fd = fd; ev->base = base; ev->flags = flags; ev->cb_func = cb; ev->cb_arg = arg; ev->ev_idx = -1; list_init(&ev->node); ev_dbg(ev, "event_set"); } /* Change base for a event */ int event_base_set(struct event_base *base, struct event *ev) { if (ev->flags & EV_ACTIVE) { errno = EINVAL; return -1; } ev->base = base; return 0; } /* Check if activated */ int is_event_active(struct event *ev) { return (ev->flags & EV_ACTIVE) ? 1 : 0; } /* de-activate event */ int event_del(struct event *ev) { struct event_base *base = ev->base; /* allow repeated deletions */ if ((ev->flags & EV_ACTIVE) == 0) { ev_dbg(ev, "event_del for inactive event??"); return 0; } ev_dbg(ev, "event_del"); /* remove from fd/signal list */ if (ev->flags & EV_SIGNAL) list_del(&ev->node); else if (ev->flags & (EV_READ | EV_WRITE)) statlist_remove(&base->fd_list, &ev->node); /* remove from timeout tree */ if (ev->flags & EV_TIMEOUT) { heap_remove(ev->base->timeout_heap, ev->timeout_idx); ev->flags &= ~EV_TIMEOUT; } /* clear reference to pollfd area */ if (ev->ev_idx >= 0) { ev->base->pfd_event[ev->ev_idx] = NULL; ev->ev_idx = -1; } /* tag inactive */ ev->flags &= ~EV_ACTIVE; return 0; } /* activate event */ int event_add(struct event *ev, struct timeval *timeout) { struct event_base *base = ev->base; Assert((ev->flags & EV_ACTIVE) == 0); Assert(base); /* sanity check, but dont do anything yet */ if (timeout) { if (ev->flags & EV_PERSIST) goto err_inval; if (!heap_reserve(base->timeout_heap, 1)) return -1; } else { if (ev->flags & EV_TIMEOUT) ev->flags &= ~EV_TIMEOUT; if (!(ev->flags & (EV_SIGNAL | EV_READ | EV_WRITE))) goto err_inval; } /* setup signal/fd */ if (ev->flags & EV_SIGNAL) { if (ev->flags & (EV_READ|EV_WRITE)) goto err_inval; if (!sig_init(base, ev->fd)) return -1; list_append(&base->sig_waiters[ev->fd], &ev->node); } else if (ev->flags & (EV_READ|EV_WRITE)) { statlist_append(&base->fd_list, &ev->node); } /* now act on timeout */ if (timeout) { ev->timeout_val = convert_timeout(base, timeout); ev->flags |= EV_TIMEOUT; heap_push(base->timeout_heap, ev); } ev->ev_idx = -1; ev->flags |= EV_ACTIVE; ev_dbg(ev, "event_add"); return 0; err_inval: errno = EINVAL; return -1; } /* * Event loop functions. */ static void deliver_event(struct event *ev, short flags) { ev_dbg(ev, "deliver_event: %d", flags); /* remove non-persitant event before calling user func */ if ((ev->flags & EV_PERSIST) == 0) event_del(ev); /* now call user func */ ev->cb_func(ev->fd, flags, ev->cb_arg); } static inline struct event *get_smallest_timeout(struct event_base *base) { return heap_top(base->timeout_heap); } /* decide how long poll() should sleep */ static int calc_timeout_ms(struct event_base *base) { struct event *ev; usec_t now; usec_t res; ev = get_smallest_timeout(base); if (!ev) return MAX_SLEEP / 1000; now = get_base_time(base); if (now + MAX_SLEEP < ev->timeout_val) res = MAX_SLEEP; else if (ev->timeout_val < now) res = 0; else res = ev->timeout_val - now; /* round up */ return (res + 999) / 1000; } /* deliver fd events */ static void process_fds(struct event_base *base, int pf_cnt) { int i; for (i = 0; i < pf_cnt; i++) { struct pollfd *pf = &base->pfd_list[i]; struct event *ev = base->pfd_event[i]; if (!ev) continue; base->pfd_event[i] = NULL; ev->ev_idx = -1; if (pf->revents & (POLLIN | POLLOUT | POLLERR | POLLHUP)) { int flags = ev->flags & (EV_READ | EV_WRITE); deliver_event(ev, flags); } if (base->loop_break) break; } } /* handle passed timeouts */ static void process_timeouts(struct event_base *base) { usec_t now; struct event *ev; ev = get_smallest_timeout(base); if (!ev) return; now = get_base_time(base); while (ev) { if (now < ev->timeout_val) break; deliver_event(ev, EV_TIMEOUT); if (base->loop_break) break; ev = get_smallest_timeout(base); } } /* main event loop */ int event_base_loop(struct event_base *base, int loop_flags) { int pf_cnt, res, timeout_ms; struct List *node; /* don't loop if non-block was requested */ if (loop_flags & EVLOOP_NONBLOCK) loop_flags |= EVLOOP_ONCE; base->loop_break = false; base->loop_exit = false; loop: if (!make_room(base, statlist_count(&base->fd_list))) return -1; /* fill pollfds */ pf_cnt = 0; statlist_for_each(node, &base->fd_list) { struct event *ev = container_of(node, struct event, node); struct pollfd *pf; ev->ev_idx = pf_cnt++; base->pfd_event[ev->ev_idx] = ev; pf = &base->pfd_list[ev->ev_idx]; pf->events = 0; pf->revents = 0; pf->fd = ev->fd; if (ev->flags & EV_READ) pf->events |= POLLIN; if (ev->flags & EV_WRITE) pf->events |= POLLOUT; } /* decide sleep time */ if (loop_flags & EVLOOP_NONBLOCK) timeout_ms = 0; else timeout_ms = calc_timeout_ms(base); /* forget cached time */ reset_base_time(base); /* poll for events */ res = poll(base->pfd_list, pf_cnt, timeout_ms); base_dbg(base, "poll(%d, timeout=%d) = res=%d errno=%d", pf_cnt, timeout_ms, res, res < 0 ? errno : 0); if (res == -1 && errno != EINTR) return -1; /* process events */ if (res > 0) { process_fds(base, pf_cnt); if (base->loop_break) return 0; } process_timeouts(base); /* decide whether to continue looping */ if (loop_flags & EVLOOP_ONCE) return 0; if (base->loop_break || base->loop_exit) return 0; goto loop; } /* * Signal handling. */ /* global signal handler registered via sigaction() */ static void uevent_sig_handler(int sig) { struct List *node, *tmp; struct event_base *base; uint8_t byte = sig; int res; if (sig < 0 || sig >= MAX_SIGNAL) return; sig_count[sig]++; list_for_each_safe(node, &sig_base_list, tmp) { base = container_of(node, struct event_base, sig_node); if (base->sig_send >= 0) { loop: res = send(base->sig_send, &byte, 1, MSG_NOSIGNAL); if (res == -1 && (errno == EINTR)) goto loop; } } } /* close signal resources on one base */ static void sig_close(struct event_base *base) { list_del(&base->sig_node); if (base->sig_send >= 0) close(base->sig_send); if (base->sig_recv >= 0) close(base->sig_recv); base->sig_recv = base->sig_send = -1; } /* call all handlers waiting for specific signal */ static void deliver_signal(struct event_base *base, int sig) { struct List *node, *tmp; list_for_each_safe(node, &base->sig_waiters[sig], tmp) { struct event *ev = container_of(node, struct event, node); deliver_event(ev, EV_SIGNAL); } } /* reader from sig socket, calls actual signal handlers */ static void sig_reader(int fd, short flags, void *arg) { struct event_base *base = arg; uint8_t buf[128]; int res, sig; /* drain the socket */ loop: res = recv(fd, buf, sizeof(buf), 0); if (res < 0) { if (errno == EINTR) goto loop; } else if ((res == sizeof(buf)) && (res > 1)) goto loop; /* now check for new signals */ for (sig = 0; sig < MAX_SIGNAL; sig++) { unsigned glob, local; if (list_empty(&base->sig_waiters[sig])) continue; glob = sig_count[sig]; local = base->sig_seen[sig]; if (glob != local) { base->sig_seen[sig] = glob; deliver_signal(base, sig); } } } /* setup signal handling for particular signal */ static bool sig_init(struct event_base *base, int sig) { int spair[2]; if (sig < 0 || sig >= MAX_SIGNAL) { errno = EINVAL; return false; } /* global handler setup */ if (!signal_set_up[sig]) { struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_handler = uevent_sig_handler; sa.sa_flags = SA_RESTART; sigfillset(&sa.sa_mask); if (sigaction(sig, &sa, &old_handler[sig]) != 0) return false; } /* local handler for base */ if (list_empty(&base->sig_node)) { if (socketpair(AF_UNIX, SOCK_STREAM, 0, spair) != 0) return false; if (!socket_setup(spair[0], true)) goto failed; if (!socket_setup(spair[1], true)) goto failed; event_assign(&base->sig_ev, base, spair[1], EV_READ|EV_PERSIST, sig_reader, base); if (event_add(&base->sig_ev, NULL) != 0) goto failed; base->sig_send = spair[0]; base->sig_recv = spair[1]; list_append(&sig_base_list, &base->sig_node); } /* if first waiter, then ignore previous signals */ if (list_empty(&base->sig_waiters[sig])) base->sig_seen[sig] = sig_count[sig]; return true; failed: close(spair[0]); close(spair[1]); return false; } /* * One-time events. */ struct once_event { struct event ev; uevent_cb_f cb_func; void *cb_arg; }; static void once_handler(int fd, short flags, void *arg) { struct once_event *once = arg; uevent_cb_f cb_func = once->cb_func; void *cb_arg = once->cb_arg; free(once); cb_func(fd, flags, cb_arg); } /* wait for one-time event, provide event struct internally */ int event_base_once(struct event_base *base, int fd, short flags, uevent_cb_f cb_func, void *cb_arg, struct timeval *timeout) { struct once_event *once; if (flags & EV_PERSIST) { errno = EINVAL; return -1; } once = calloc(1, sizeof(*once)); if (!once) return -1; event_assign(&once->ev, base, fd, flags, once_handler, once); if (event_add(&once->ev, timeout) != 0) { free(once); return -1; } return 0; } /* * Stop loop at particular time. */ static void loopexit_handler(int fd, short flags, void *arg) { struct event_base *base = arg; base->loop_exit = true; } int event_base_loopexit(struct event_base *base, struct timeval *timeout) { if (!timeout) { errno = EINVAL; return -1; } return event_base_once(base, -1, 0, loopexit_handler, base, timeout); } /* * Info */ const char *event_get_version(void) { return "usual/event"; } const char *event_get_method(void) { return "poll"; } #endif /* !HAVE_LIBEVENT */ pgbouncer-1.7/lib/usual/json.c0000664000175000017500000011307112560223470013310 00000000000000/* * Read and write JSON. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #define TYPE_BITS 3 #define TYPE_MASK ((1 << TYPE_BITS) - 1) #define UNATTACHED ((struct JsonValue *)(1 << TYPE_BITS)) #define JSON_MAX_KEY (1024*1024) #define NUMBER_BUF 100 #define JSON_MAXINT ((1LL << 53) - 1) #define JSON_MININT (-(1LL << 53) + 1) /* * Common struct for all JSON values */ struct JsonValue { /* actual value for simple types */ union { double v_float; /* float */ int64_t v_int; /* int */ bool v_bool; /* bool */ size_t v_size; /* str/list/dict */ } u; /* pointer to next elem and type in low bits */ uintptr_t v_next_and_type; }; /* * List container. */ struct ValueList { struct JsonValue *first; struct JsonValue *last; struct JsonValue **array; }; /* * Extra data for list/dict. */ struct JsonContainer { /* parent container */ struct JsonValue *c_parent; /* main context for child alloc */ struct JsonContext *c_ctx; /* child elements */ union { struct CBTree *c_dict; struct ValueList c_list; } u; }; #define DICT_EXTRA (offsetof(struct JsonContainer, u.c_dict) + sizeof(struct CBTree *)) #define LIST_EXTRA (sizeof(struct JsonContainer)) /* * Allocation context. */ struct JsonContext { CxMem *pool; unsigned int options; /* parse state */ struct JsonValue *parent; struct JsonValue *cur_key; struct JsonValue *top; const char *lasterr; char errbuf[128]; int64_t linenr; }; struct RenderState { struct MBuf *dst; unsigned int options; }; /* * Parser states */ enum ParseState { S_INITIAL_VALUE = 1, S_LIST_VALUE, S_LIST_VALUE_OR_CLOSE, S_LIST_COMMA_OR_CLOSE, S_DICT_KEY, S_DICT_KEY_OR_CLOSE, S_DICT_COLON, S_DICT_VALUE, S_DICT_COMMA_OR_CLOSE, S_PARENT, S_DONE, MAX_STATES, }; /* * Tokens that change state. */ enum TokenTypes { T_STRING, T_OTHER, T_COMMA, T_COLON, T_OPEN_DICT, T_OPEN_LIST, T_CLOSE_DICT, T_CLOSE_LIST, MAX_TOKENS }; /* * 4-byte ints for small string tokens. */ #define C_NULL FOURCC('n','u','l','l') #define C_TRUE FOURCC('t','r','u','e') #define C_ALSE FOURCC('a','l','s','e') /* * Signature for render functions. */ typedef bool (*render_func_t)(struct RenderState *rs, struct JsonValue *jv); static bool render_any(struct RenderState *rs, struct JsonValue *jv); /* * Header manipulation */ static inline enum JsonValueType get_type(struct JsonValue *jv) { return jv->v_next_and_type & TYPE_MASK; } static inline bool has_type(struct JsonValue *jv, enum JsonValueType type) { if (!jv) return false; return get_type(jv) == type; } static inline struct JsonValue *get_next(struct JsonValue *jv) { return (struct JsonValue *)(jv->v_next_and_type & ~(uintptr_t)TYPE_MASK); } static inline void set_next(struct JsonValue *jv, struct JsonValue *next) { jv->v_next_and_type = (uintptr_t)next | get_type(jv); } static inline bool is_unattached(struct JsonValue *jv) { return get_next(jv) == UNATTACHED; } static inline void *get_extra(struct JsonValue *jv) { return (void *)(jv + 1); } static inline char *get_cstring(struct JsonValue *jv) { enum JsonValueType type = get_type(jv); if (type != JSON_STRING) return NULL; return get_extra(jv); } /* * Collection header manipulation. */ static inline struct JsonContainer *get_container(struct JsonValue *jv) { enum JsonValueType type = get_type(jv); if (type != JSON_DICT && type != JSON_LIST) return NULL; return get_extra(jv); } static inline void set_parent(struct JsonValue *jv, struct JsonValue *parent) { struct JsonContainer *c = get_container(jv); if (c) c->c_parent = parent; } static inline struct JsonContext *get_context(struct JsonValue *jv) { struct JsonContainer *c = get_container(jv); return c ? c->c_ctx : NULL; } static inline struct CBTree *get_dict_tree(struct JsonValue *jv) { struct JsonContainer *c; if (has_type(jv, JSON_DICT)) { c = get_container(jv); return c->u.c_dict; } return NULL; } static inline struct ValueList *get_list_vlist(struct JsonValue *jv) { struct JsonContainer *c; if (has_type(jv, JSON_LIST)) { c = get_container(jv); return &c->u.c_list; } return NULL; } /* * Random helpers */ /* copy and return final pointer */ static inline char *plain_copy(char *dst, const char *src, const char *endptr) { if (src < endptr) { memcpy(dst, src, endptr - src); return dst + (endptr - src); } return dst; } /* error message on context */ _PRINTF(2,0) static void format_err(struct JsonContext *ctx, const char *errmsg, va_list ap) { char buf[128]; if (ctx->lasterr) return; vsnprintf(buf, sizeof(buf), errmsg, ap); snprintf(ctx->errbuf, sizeof(ctx->errbuf), "Line #%" PRIi64 ": %s", ctx->linenr, buf); ctx->lasterr = ctx->errbuf; } /* set message and return false */ _PRINTF(2,3) static bool err_false(struct JsonContext *ctx, const char *errmsg, ...) { va_list ap; va_start(ap, errmsg); format_err(ctx, errmsg, ap); va_end(ap); return false; } /* set message and return NULL */ _PRINTF(2,3) static void *err_null(struct JsonContext *ctx, const char *errmsg, ...) { va_list ap; va_start(ap, errmsg); format_err(ctx, errmsg, ap); va_end(ap); return NULL; } /* callback for cbtree, returns key bytes */ static size_t get_key_data_cb(void *dictptr, void *keyptr, const void **dst_p) { struct JsonValue *key = keyptr; *dst_p = get_cstring(key); return key->u.v_size; } /* add elemnt to list */ static void real_list_append(struct JsonValue *list, struct JsonValue *elem) { struct ValueList *vlist; vlist = get_list_vlist(list); if (vlist->last) { set_next(vlist->last, elem); } else { vlist->first = elem; } vlist->last = elem; vlist->array = NULL; list->u.v_size++; } /* add key to tree */ static bool real_dict_add_key(struct JsonContext *ctx, struct JsonValue *dict, struct JsonValue *key) { struct CBTree *tree; tree = get_dict_tree(dict); if (!tree) return err_false(ctx, "Expect dict"); if (json_value_size(key) > JSON_MAX_KEY) return err_false(ctx, "Too large key"); dict->u.v_size++; if (!cbtree_insert(tree, key)) return err_false(ctx, "Key insertion failed"); return true; } /* create basic value struct, link to stuctures */ static struct JsonValue *mk_value(struct JsonContext *ctx, enum JsonValueType type, size_t extra, bool attach) { struct JsonValue *val; struct JsonContainer *col = NULL; if (!ctx) return NULL; val = cx_alloc(ctx->pool, sizeof(struct JsonValue) + extra); if (!val) return err_null(ctx, "No memory"); if ((uintptr_t)val & TYPE_MASK) return err_null(ctx, "Unaligned pointer"); /* initial value */ val->v_next_and_type = type; val->u.v_int = 0; if (type == JSON_DICT || type == JSON_LIST) { col = get_container(val); col->c_ctx = ctx; col->c_parent = NULL; if (type == JSON_DICT) { col->u.c_dict = cbtree_create(get_key_data_cb, NULL, val, ctx->pool); if (!col->u.c_dict) return err_null(ctx, "No memory"); } else { memset(&col->u.c_list, 0, sizeof(col->u.c_list)); } } /* independent JsonValue? */ if (!attach) { set_next(val, UNATTACHED); return val; } /* attach to parent */ if (col) col->c_parent = ctx->parent; /* attach to previous value */ if (has_type(ctx->parent, JSON_DICT)) { if (ctx->cur_key) { set_next(ctx->cur_key, val); ctx->cur_key = NULL; } else { ctx->cur_key = val; } } else if (has_type(ctx->parent, JSON_LIST)) { real_list_append(ctx->parent, val); } else if (!ctx->top) { ctx->top = val; } else { return err_null(ctx, "Only one top element is allowed"); } return val; } static void prepare_array(struct JsonValue *list) { struct JsonContainer *c; struct JsonValue *val; struct ValueList *vlist; size_t i; vlist = get_list_vlist(list); if (vlist->array) return; c = get_container(list); vlist->array = cx_alloc(c->c_ctx->pool, list->u.v_size * sizeof(struct JsonValue *)); if (!vlist->array) return; val = vlist->first; for (i = 0; i < list->u.v_size && val; i++) { vlist->array[i] = val; val = get_next(val); } } /* * Parsing code starts */ /* create and change context */ static bool open_container(struct JsonContext *ctx, enum JsonValueType type, unsigned int extra) { struct JsonValue *jv; jv = mk_value(ctx, type, extra, true); if (!jv) return false; ctx->parent = jv; ctx->cur_key = NULL; return true; } /* close and change context */ static enum ParseState close_container(struct JsonContext *ctx, enum ParseState state) { struct JsonContainer *c; if (state != S_PARENT) return (int)err_false(ctx, "close_container bug"); c = get_container(ctx->parent); if (!c) return (int)err_false(ctx, "invalid parent"); ctx->parent = c->c_parent; ctx->cur_key = NULL; if (has_type(ctx->parent, JSON_DICT)) { return S_DICT_COMMA_OR_CLOSE; } else if (has_type(ctx->parent, JSON_LIST)) { return S_LIST_COMMA_OR_CLOSE; } return S_DONE; } /* parse 4-char token */ static bool parse_char4(struct JsonContext *ctx, const char **src_p, const char *end, uint32_t t_exp, enum JsonValueType type, bool val) { const char *src; uint32_t t_got; struct JsonValue *jv; src = *src_p; if (src + 4 > end) return err_false(ctx, "Unexpected end of token"); memcpy(&t_got, src, 4); if (t_exp != t_got) return err_false(ctx, "Invalid token"); jv = mk_value(ctx, type, 0, true); if (!jv) return false; jv->u.v_bool = val; *src_p += 4; return true; } /* parse int or float */ static bool parse_number(struct JsonContext *ctx, const char **src_p, const char *end) { const char *start, *src; enum JsonValueType type = JSON_INT; char *tokend = NULL; char buf[NUMBER_BUF]; size_t len; struct JsonValue *jv; double v_float = 0; int64_t v_int = 0; /* scan & copy */ start = src = *src_p; for (; src < end; src++) { if (*src >= '0' && *src <= '9') { } else if (*src == '+' || *src == '-') { } else if (*src == '.' || *src == 'e' || *src == 'E') { type = JSON_FLOAT; } else { break; } } len = src - start; if (len >= NUMBER_BUF) goto failed; memcpy(buf, start, len); buf[len] = 0; /* now parse */ errno = 0; tokend = buf; if (type == JSON_FLOAT) { v_float = strtod_dot(buf, &tokend); if (*tokend != 0 || errno || !isfinite(v_float)) goto failed; } else if (len < 8) { v_int = strtol(buf, &tokend, 10); if (*tokend != 0 || errno) goto failed; } else { v_int = strtoll(buf, &tokend, 10); if (*tokend != 0 || errno || v_int < JSON_MININT || v_int > JSON_MAXINT) goto failed; } /* create value struct */ jv = mk_value(ctx, type, 0, true); if (!jv) return false; if (type == JSON_FLOAT) { jv->u.v_float = v_float; } else { jv->u.v_int = v_int; } *src_p = src; return true; failed: if (!errno) errno = EINVAL; return err_false(ctx, "Number parse failed"); } /* * String parsing */ static int parse_hex(const char *s, const char *end) { int v = 0, c, i, x; if (s + 4 > end) return -1; for (i = 0; i < 4; i++) { c = s[i]; if (c >= '0' && c <= '9') { x = c - '0'; } else if (c >= 'a' && c <= 'f') { x = c - 'a' + 10; } else if (c >= 'A' && c <= 'F') { x = c - 'A' + 10; } else { return -1; } v = (v << 4) | x; } return v; } /* process \uXXXX escapes, merge surrogates */ static bool parse_uescape(struct JsonContext *ctx, char **dst_p, char *dstend, const char **src_p, const char *end) { int c, c2; const char *src = *src_p; c = parse_hex(src, end); if (c <= 0) return err_false(ctx, "Invalid hex escape"); src += 4; if (c >= 0xD800 && c <= 0xDFFF) { /* first surrogate */ if (c >= 0xDC00) return err_false(ctx, "Invalid UTF16 escape"); if (src + 6 > end) return err_false(ctx, "Invalid UTF16 escape"); /* second surrogate */ if (src[0] != '\\' || src[1] != 'u') return err_false(ctx, "Invalid UTF16 escape"); c2 = parse_hex(src + 2, end); if (c2 < 0xDC00 || c2 > 0xDFFF) return err_false(ctx, "Invalid UTF16 escape"); c = 0x10000 + ((c & 0x3FF) << 10) + (c2 & 0x3FF); src += 6; } /* now write char */ if (!utf8_put_char(c, dst_p, dstend)) return err_false(ctx, "Invalid UTF16 escape"); *src_p = src; return true; } #define meta_string(c) (((c) == '"' || (c) == '\\' || (c) == '\0' || \ (c) == '\n' || ((c) & 0x80) != 0) ? 1 : 0) static const uint8_t string_examine_chars[] = INTMAP256_CONST(meta_string); /* look for string end, validate contents */ static bool scan_string(struct JsonContext *ctx, const char *src, const char *end, const char **str_end_p, bool *hasesc_p, int64_t *nlines_p) { bool hasesc = false; int64_t lines = 0; unsigned int n; bool check_utf8 = true; if (ctx->options & JSON_PARSE_IGNORE_ENCODING) check_utf8 = false; while (src < end) { if (!string_examine_chars[(uint8_t)*src]) { src++; } else if (*src == '"') { /* string end */ *hasesc_p = hasesc; *str_end_p = src; *nlines_p = lines; return true; } else if (*src == '\\') { hasesc = true; src++; if (src < end && (*src == '\\' || *src == '"')) src++; } else if (*src & 0x80) { n = utf8_validate_seq(src, end); if (n) { src += n; } else if (check_utf8) { goto badutf; } else { src++; } } else if (*src == '\n') { lines++; src++; } else { goto badutf; } } return err_false(ctx, "Unexpected end of string"); badutf: return err_false(ctx, "Invalid UTF8 sequence"); } /* string boundaries are known, copy and unescape */ static char *process_escapes(struct JsonContext *ctx, const char *src, const char *end, char *dst, char *dstend) { const char *esc; /* process escapes */ while (src < end) { esc = memchr(src, '\\', end - src); if (!esc) { dst = plain_copy(dst, src, end); break; } dst = plain_copy(dst, src, esc); src = esc + 1; switch (*src++) { case '"': *dst++ = '"'; break; case '\\': *dst++ = '\\'; break; case '/': *dst++ = '/'; break; case 'b': *dst++ = '\b'; break; case 'f': *dst++ = '\f'; break; case 'n': *dst++ = '\n'; break; case 'r': *dst++ = '\r'; break; case 't': *dst++ = '\t'; break; case 'u': if (!parse_uescape(ctx, &dst, dstend, &src, end)) return NULL; break; default: return err_null(ctx, "Invalid escape code"); } } return dst; } /* 2-phase string processing */ static bool parse_string(struct JsonContext *ctx, const char **src_p, const char *end) { const char *start, *strend = NULL; bool hasesc = false; char *dst, *dstend; size_t len; struct JsonValue *jv; int64_t lines = 0; /* find string boundaries, validate */ start = *src_p; if (!scan_string(ctx, start, end, &strend, &hasesc, &lines)) return false; /* create value struct */ len = strend - start; jv = mk_value(ctx, JSON_STRING, len + 1, true); if (!jv) return false; dst = get_cstring(jv); dstend = dst + len; /* copy & process escapes */ if (hasesc) { dst = process_escapes(ctx, start, strend, dst, dstend); if (!dst) return false; } else { dst = plain_copy(dst, start, strend); } *dst = '\0'; jv->u.v_size = dst - get_cstring(jv); ctx->linenr += lines; *src_p = strend + 1; return true; } /* * Helpers for relaxed parsing */ static bool skip_comment(struct JsonContext *ctx, const char **src_p, const char *end) { const char *s, *start; char c; size_t lnr; s = start = *src_p; if (s >= end) return false; c = *s++; if (c == '/') { s = memchr(s, '\n', end - s); if (s) { ctx->linenr++; *src_p = s + 1; } else { *src_p = end; } return true; } else if (c == '*') { for (lnr = 0; s + 2 <= end; s++) { if (s[0] == '*' && s[1] == '/') { ctx->linenr += lnr; *src_p = s + 2; return true; } else if (s[0] == '\n') { lnr++; } } } return false; } static bool skip_extra_comma(struct JsonContext *ctx, const char **src_p, const char *end, enum ParseState state) { bool skip = false; const char *src = *src_p; while (src < end && isspace(*src)) { if (*src == '\n') ctx->linenr++; src++; } if (src < end) { if (*src == '}') { if (state == S_DICT_COMMA_OR_CLOSE || state == S_DICT_KEY_OR_CLOSE) skip = true; } else if (*src == ']') { if (state == S_LIST_COMMA_OR_CLOSE || state == S_LIST_VALUE_OR_CLOSE) skip = true; } } *src_p = src; return skip; } /* * Main parser */ /* oldstate + token -> newstate */ static const unsigned char STATE_STEPS[MAX_STATES][MAX_TOKENS] = { [S_INITIAL_VALUE] = { [T_OPEN_LIST] = S_LIST_VALUE_OR_CLOSE, [T_OPEN_DICT] = S_DICT_KEY_OR_CLOSE, [T_STRING] = S_DONE, [T_OTHER] = S_DONE }, [S_LIST_VALUE] = { [T_OPEN_LIST] = S_LIST_VALUE_OR_CLOSE, [T_OPEN_DICT] = S_DICT_KEY_OR_CLOSE, [T_STRING] = S_LIST_COMMA_OR_CLOSE, [T_OTHER] = S_LIST_COMMA_OR_CLOSE }, [S_LIST_VALUE_OR_CLOSE] = { [T_OPEN_LIST] = S_LIST_VALUE_OR_CLOSE, [T_OPEN_DICT] = S_DICT_KEY_OR_CLOSE, [T_STRING] = S_LIST_COMMA_OR_CLOSE, [T_OTHER] = S_LIST_COMMA_OR_CLOSE, [T_CLOSE_LIST] = S_PARENT }, [S_LIST_COMMA_OR_CLOSE] = { [T_COMMA] = S_LIST_VALUE, [T_CLOSE_LIST] = S_PARENT }, [S_DICT_KEY] = { [T_STRING] = S_DICT_COLON }, [S_DICT_KEY_OR_CLOSE] = { [T_STRING] = S_DICT_COLON, [T_CLOSE_DICT] = S_PARENT }, [S_DICT_COLON] = { [T_COLON] = S_DICT_VALUE }, [S_DICT_VALUE] = { [T_OPEN_LIST] = S_LIST_VALUE_OR_CLOSE, [T_OPEN_DICT] = S_DICT_KEY_OR_CLOSE, [T_STRING] = S_DICT_COMMA_OR_CLOSE, [T_OTHER] = S_DICT_COMMA_OR_CLOSE }, [S_DICT_COMMA_OR_CLOSE] = { [T_COMMA] = S_DICT_KEY, [T_CLOSE_DICT] = S_PARENT }, }; #define MAPSTATE(state, tok) do { \ int newstate = STATE_STEPS[state][tok]; \ if (!newstate) \ return err_false(ctx, "Unexpected symbol: '%c'", c); \ state = newstate; \ } while (0) /* actual parser */ static bool parse_tokens(struct JsonContext *ctx, const char *src, const char *end) { char c; enum ParseState state = S_INITIAL_VALUE; bool relaxed = ctx->options & JSON_PARSE_RELAXED; while (src < end) { c = *src++; switch (c) { case '\n': ctx->linenr++; case ' ': case '\t': case '\r': case '\f': case '\v': /* common case - many spaces */ while (src < end && *src == ' ') src++; break; case '"': MAPSTATE(state, T_STRING); if (!parse_string(ctx, &src, end)) goto failed; break; case 'n': MAPSTATE(state, T_OTHER); src--; if (!parse_char4(ctx, &src, end, C_NULL, JSON_NULL, 0)) goto failed; continue; case 't': MAPSTATE(state, T_OTHER); src--; if (!parse_char4(ctx, &src, end, C_TRUE, JSON_BOOL, 1)) goto failed; break; case 'f': MAPSTATE(state, T_OTHER); if (!parse_char4(ctx, &src, end, C_ALSE, JSON_BOOL, 0)) goto failed; break; case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': MAPSTATE(state, T_OTHER); src--; if (!parse_number(ctx, &src, end)) goto failed; break; case '[': MAPSTATE(state, T_OPEN_LIST); if (!open_container(ctx, JSON_LIST, LIST_EXTRA)) goto failed; break; case '{': MAPSTATE(state, T_OPEN_DICT); if (!open_container(ctx, JSON_DICT, DICT_EXTRA)) goto failed; break; case ']': MAPSTATE(state, T_CLOSE_LIST); state = close_container(ctx, state); if (!state) goto failed; break; case '}': MAPSTATE(state, T_CLOSE_DICT); state = close_container(ctx, state); if (!state) goto failed; break; case ':': MAPSTATE(state, T_COLON); if (!real_dict_add_key(ctx, ctx->parent, ctx->cur_key)) goto failed; break; case ',': if (relaxed && skip_extra_comma(ctx, &src, end, state)) continue; MAPSTATE(state, T_COMMA); break; case '/': if (relaxed && skip_comment(ctx, &src, end)) continue; default: return err_false(ctx, "Invalid symbol: '%c'", c); } } if (state != S_DONE) return err_false(ctx, "Container still open"); return true; failed: return false; } /* parser public api */ struct JsonValue *json_parse(struct JsonContext *ctx, const char *json, size_t len) { const char *end = json + len; /* reset parser */ ctx->linenr = 1; ctx->parent = NULL; ctx->cur_key = NULL; ctx->lasterr = NULL; ctx->top = NULL; if (!parse_tokens(ctx, json, end)) return NULL; return ctx->top; } /* * Render value as JSON string. */ static bool render_null(struct RenderState *rs, struct JsonValue *jv) { return mbuf_write(rs->dst, "null", 4); } static bool render_bool(struct RenderState *rs, struct JsonValue *jv) { if (jv->u.v_bool) return mbuf_write(rs->dst, "true", 4); return mbuf_write(rs->dst, "false", 5); } static bool render_int(struct RenderState *rs, struct JsonValue *jv) { char buf[NUMBER_BUF]; int len; len = snprintf(buf, sizeof(buf), "%" PRIi64, jv->u.v_int); if (len < 0 || len >= NUMBER_BUF) return false; return mbuf_write(rs->dst, buf, len); } static bool render_float(struct RenderState *rs, struct JsonValue *jv) { char buf[NUMBER_BUF + 2]; int len; len = dtostr_dot(buf, NUMBER_BUF, jv->u.v_float); if (len < 0 || len >= NUMBER_BUF) return false; if (!memchr(buf, '.', len) && !memchr(buf, 'e', len)) { buf[len++] = '.'; buf[len++] = '0'; } return mbuf_write(rs->dst, buf, len); } static bool escape_char(struct MBuf *dst, unsigned int c) { char ec; char buf[10]; /* start escape */ if (!mbuf_write_byte(dst, '\\')) return false; /* escape same char */ if (c == '"' || c == '\\') return mbuf_write_byte(dst, c); /* low-ascii mess */ switch (c) { case '\b': ec = 'b'; break; case '\f': ec = 'f'; break; case '\n': ec = 'n'; break; case '\r': ec = 'r'; break; case '\t': ec = 't'; break; default: snprintf(buf, sizeof(buf), "u%04x", c); return mbuf_write(dst, buf, 5); } return mbuf_write_byte(dst, ec); } static bool render_string(struct RenderState *rs, struct JsonValue *jv) { const char *s, *last; const char *val = get_cstring(jv); size_t len = jv->u.v_size; const char *end = val + len; unsigned int c; /* start quote */ if (!mbuf_write_byte(rs->dst, '"')) return false; for (s = last = val; s < end; s++) { if (*s == '"' || *s == '\\' || (unsigned char)*s < 0x20 || /* Valid in JSON, but not in JS: \u2028 - Line separator \u2029 - Paragraph separator */ ((unsigned char)s[0] == 0xE2 && (unsigned char)s[1] == 0x80 && ((unsigned char)s[2] == 0xA8 || (unsigned char)s[2] == 0xA9))) { /* flush */ if (last < s) { if (!mbuf_write(rs->dst, last, s - last)) return false; } if ((unsigned char)s[0] == 0xE2) { c = 0x2028 + ((unsigned char)s[2] - 0xA8); last = s + 3; } else { c = (unsigned char)*s; last = s + 1; } /* output escaped char */ if (!escape_char(rs->dst, c)) return false; } } /* flush */ if (last < s) { if (!mbuf_write(rs->dst, last, s - last)) return false; } /* final quote */ if (!mbuf_write_byte(rs->dst, '"')) return false; return true; } /* * Render complex values */ struct ElemWriterState { struct RenderState *rs; char sep; }; static bool list_elem_writer(void *arg, struct JsonValue *elem) { struct ElemWriterState *state = arg; if (state->sep && !mbuf_write_byte(state->rs->dst, state->sep)) return false; state->sep = ','; return render_any(state->rs, elem); } static bool render_list(struct RenderState *rs, struct JsonValue *list) { struct ElemWriterState state; state.rs = rs; state.sep = 0; if (!mbuf_write_byte(rs->dst, '[')) return false; if (!json_list_iter(list, list_elem_writer, &state)) return false; if (!mbuf_write_byte(rs->dst, ']')) return false; return true; } static bool dict_elem_writer(void *ctx, struct JsonValue *key, struct JsonValue *val) { struct ElemWriterState *state = ctx; if (state->sep && !mbuf_write_byte(state->rs->dst, state->sep)) return false; state->sep = ','; if (!render_any(state->rs, key)) return false; if (!mbuf_write_byte(state->rs->dst, ':')) return false; return render_any(state->rs, val); } static bool render_dict(struct RenderState *rs, struct JsonValue *dict) { struct ElemWriterState state; state.rs = rs; state.sep = 0; if (!mbuf_write_byte(rs->dst, '{')) return false; if (!json_dict_iter(dict, dict_elem_writer, &state)) return false; if (!mbuf_write_byte(rs->dst, '}')) return false; return true; } static bool render_invalid(struct RenderState *rs, struct JsonValue *jv) { return false; } /* * Public api */ static bool render_any(struct RenderState *rs, struct JsonValue *jv) { static const render_func_t rfunc_map[] = { render_invalid, render_null, render_bool, render_int, render_float, render_string, render_list, render_dict, }; return rfunc_map[get_type(jv)](rs, jv); } bool json_render(struct MBuf *dst, struct JsonValue *jv) { struct RenderState rs; rs.dst = dst; rs.options = 0; return render_any(&rs, jv); } /* * Examine single value */ enum JsonValueType json_value_type(struct JsonValue *jv) { return get_type(jv); } size_t json_value_size(struct JsonValue *jv) { if (has_type(jv, JSON_STRING) || has_type(jv, JSON_LIST) || has_type(jv, JSON_DICT)) return jv->u.v_size; return 0; } bool json_value_as_bool(struct JsonValue *jv, bool *dst_p) { if (!has_type(jv, JSON_BOOL)) return false; *dst_p = jv->u.v_bool; return true; } bool json_value_as_int(struct JsonValue *jv, int64_t *dst_p) { if (!has_type(jv, JSON_INT)) return false; *dst_p = jv->u.v_int; return true; } bool json_value_as_float(struct JsonValue *jv, double *dst_p) { if (!has_type(jv, JSON_FLOAT)) { if (has_type(jv, JSON_INT)) { *dst_p = jv->u.v_int; return true; } return false; } *dst_p = jv->u.v_float; return true; } bool json_value_as_string(struct JsonValue *jv, const char **dst_p, size_t *size_p) { if (!has_type(jv, JSON_STRING)) return false; *dst_p = get_cstring(jv); if (size_p) *size_p = jv->u.v_size; return true; } /* * Load value from dict. */ static int dict_getter(struct JsonValue *dict, const char *key, unsigned int klen, struct JsonValue **val_p, enum JsonValueType req_type, bool req_value) { struct JsonValue *val, *kjv; struct CBTree *tree; tree = get_dict_tree(dict); if (!tree) return false; kjv = cbtree_lookup(tree, key, klen); if (!kjv) { if (req_value) return false; *val_p = NULL; return true; } val = get_next(kjv); if (!req_value && json_value_is_null(val)) { *val_p = NULL; return true; } if (!has_type(val, req_type)) return false; *val_p = val; return true; } bool json_dict_get_value(struct JsonValue *dict, const char *key, struct JsonValue **val_p) { struct CBTree *tree; struct JsonValue *kjv; size_t klen; tree = get_dict_tree(dict); if (!tree) return false; klen = strlen(key); kjv = cbtree_lookup(tree, key, klen); if (!kjv) return false; *val_p = get_next(kjv); return true; } bool json_dict_is_null(struct JsonValue *dict, const char *key) { struct JsonValue *val; if (!json_dict_get_value(dict, key, &val)) return true; return has_type(val, JSON_NULL); } bool json_dict_get_bool(struct JsonValue *dict, const char *key, bool *dst_p) { struct JsonValue *val; if (!dict_getter(dict, key, strlen(key), &val, JSON_BOOL, true)) return false; return json_value_as_bool(val, dst_p); } bool json_dict_get_int(struct JsonValue *dict, const char *key, int64_t *dst_p) { struct JsonValue *val; if (!dict_getter(dict, key, strlen(key), &val, JSON_INT, true)) return false; return json_value_as_int(val, dst_p); } bool json_dict_get_float(struct JsonValue *dict, const char *key, double *dst_p) { struct JsonValue *val; if (!dict_getter(dict, key, strlen(key), &val, JSON_FLOAT, true)) return false; return json_value_as_float(val, dst_p); } bool json_dict_get_string(struct JsonValue *dict, const char *key, const char **dst_p, size_t *len_p) { struct JsonValue *val; if (!dict_getter(dict, key, strlen(key), &val, JSON_STRING, true)) return false; return json_value_as_string(val, dst_p, len_p); } bool json_dict_get_list(struct JsonValue *dict, const char *key, struct JsonValue **dst_p) { return dict_getter(dict, key, strlen(key), dst_p, JSON_LIST, true); } bool json_dict_get_dict(struct JsonValue *dict, const char *key, struct JsonValue **dst_p) { return dict_getter(dict, key, strlen(key), dst_p, JSON_DICT, true); } /* * Load optional dict element. */ bool json_dict_get_opt_bool(struct JsonValue *dict, const char *key, bool *dst_p) { struct JsonValue *val; if (!dict_getter(dict, key, strlen(key), &val, JSON_BOOL, false)) return false; return !val || json_value_as_bool(val, dst_p); } bool json_dict_get_opt_int(struct JsonValue *dict, const char *key, int64_t *dst_p) { struct JsonValue *val; if (!dict_getter(dict, key, strlen(key), &val, JSON_INT, false)) return false; return !val || json_value_as_int(val, dst_p); } bool json_dict_get_opt_float(struct JsonValue *dict, const char *key, double *dst_p) { struct JsonValue *val; if (!dict_getter(dict, key, strlen(key), &val, JSON_FLOAT, false)) return false; return !val || json_value_as_float(val, dst_p); } bool json_dict_get_opt_string(struct JsonValue *dict, const char *key, const char **dst_p, size_t *len_p) { struct JsonValue *val; if (!dict_getter(dict, key, strlen(key), &val, JSON_STRING, false)) return false; return !val || json_value_as_string(val, dst_p, len_p); } bool json_dict_get_opt_list(struct JsonValue *dict, const char *key, struct JsonValue **dst_p) { struct JsonValue *val; if (!dict_getter(dict, key, strlen(key), &val, JSON_LIST, false)) return false; if (val) *dst_p = val; return true; } bool json_dict_get_opt_dict(struct JsonValue *dict, const char *key, struct JsonValue **dst_p) { struct JsonValue *val; if (!dict_getter(dict, key, strlen(key), &val, JSON_DICT, false)) return false; if (val) *dst_p = val; return true; } /* * Load value from list. */ bool json_list_get_value(struct JsonValue *list, size_t index, struct JsonValue **val_p) { struct JsonValue *val; struct ValueList *vlist; size_t i; vlist = get_list_vlist(list); if (!vlist) return false; if (index >= list->u.v_size) return false; if (!vlist->array && list->u.v_size > 10) prepare_array(list); /* direct fetch */ if (vlist->array) { *val_p = vlist->array[index]; return true; } /* walk */ val = vlist->first; for (i = 0; val; i++) { if (i == index) { *val_p = val; return true; } val = get_next(val); } return false; } bool json_list_is_null(struct JsonValue *list, size_t n) { struct JsonValue *jv; if (!json_list_get_value(list, n, &jv)) return true; return has_type(jv, JSON_NULL); } bool json_list_get_bool(struct JsonValue *list, size_t index, bool *val_p) { struct JsonValue *jv; if (!json_list_get_value(list, index, &jv)) return false; return json_value_as_bool(jv, val_p); } bool json_list_get_int(struct JsonValue *list, size_t index, int64_t *val_p) { struct JsonValue *jv; if (!json_list_get_value(list, index, &jv)) return false; return json_value_as_int(jv, val_p); } bool json_list_get_float(struct JsonValue *list, size_t index, double *val_p) { struct JsonValue *jv; if (!json_list_get_value(list, index, &jv)) return false; return json_value_as_float(jv, val_p); } bool json_list_get_string(struct JsonValue *list, size_t index, const char **val_p, size_t *len_p) { struct JsonValue *jv; if (!json_list_get_value(list, index, &jv)) return false; return json_value_as_string(jv, val_p, len_p); } bool json_list_get_list(struct JsonValue *list, size_t index, struct JsonValue **val_p) { struct JsonValue *jv; if (!json_list_get_value(list, index, &jv)) return false; if (!has_type(jv, JSON_LIST)) return false; *val_p = jv; return true; } bool json_list_get_dict(struct JsonValue *list, size_t index, struct JsonValue **val_p) { struct JsonValue *jv; if (!json_list_get_value(list, index, &jv)) return false; if (!has_type(jv, JSON_DICT)) return false; *val_p = jv; return true; } /* * Iterate over list and dict values. */ struct DictIterState { json_dict_iter_callback_f cb_func; void *cb_arg; }; static bool dict_iter_helper(void *arg, void *jv) { struct DictIterState *state = arg; struct JsonValue *key = jv; struct JsonValue *val = get_next(key); return state->cb_func(state->cb_arg, key, val); } bool json_dict_iter(struct JsonValue *dict, json_dict_iter_callback_f cb_func, void *cb_arg) { struct DictIterState state; struct CBTree *tree; tree = get_dict_tree(dict); if (!tree) return false; state.cb_func = cb_func; state.cb_arg = cb_arg; return cbtree_walk(tree, dict_iter_helper, &state); } bool json_list_iter(struct JsonValue *list, json_list_iter_callback_f cb_func, void *cb_arg) { struct JsonValue *elem; struct ValueList *vlist; vlist = get_list_vlist(list); if (!vlist) return false; for (elem = vlist->first; elem; elem = get_next(elem)) { if (!cb_func(cb_arg, elem)) return false; } return true; } /* * Create new values. */ struct JsonValue *json_new_null(struct JsonContext *ctx) { return mk_value(ctx, JSON_NULL, 0, false); } struct JsonValue *json_new_bool(struct JsonContext *ctx, bool val) { struct JsonValue *jv; jv = mk_value(ctx, JSON_BOOL, 0, false); if (jv) jv->u.v_bool = val; return jv; } struct JsonValue *json_new_int(struct JsonContext *ctx, int64_t val) { struct JsonValue *jv; if (val < JSON_MININT || val > JSON_MAXINT) { errno = ERANGE; return NULL; } jv = mk_value(ctx, JSON_INT, 0, false); if (jv) jv->u.v_int = val; return jv; } struct JsonValue *json_new_float(struct JsonContext *ctx, double val) { struct JsonValue *jv; /* check if value survives JSON roundtrip */ if (!isfinite(val)) return false; jv = mk_value(ctx, JSON_FLOAT, 0, false); if (jv) jv->u.v_float = val; return jv; } struct JsonValue *json_new_string(struct JsonContext *ctx, const char *val) { struct JsonValue *jv; size_t len; len = strlen(val); if (!utf8_validate_string(val, val + len)) return NULL; jv = mk_value(ctx, JSON_STRING, len + 1, false); if (jv) { memcpy(get_cstring(jv), val, len + 1); jv->u.v_size = len; } return jv; } struct JsonValue *json_new_list(struct JsonContext *ctx) { return mk_value(ctx, JSON_LIST, LIST_EXTRA, false); } struct JsonValue *json_new_dict(struct JsonContext *ctx) { return mk_value(ctx, JSON_DICT, DICT_EXTRA, false); } /* * Add to containers */ bool json_list_append(struct JsonValue *list, struct JsonValue *val) { if (!val) return false; if (!has_type(list, JSON_LIST)) return false; if (!is_unattached(val)) return false; set_parent(val, list); set_next(val, NULL); real_list_append(list, val); return true; } bool json_list_append_null(struct JsonValue *list) { struct JsonValue *v; v = json_new_null(get_context(list)); return json_list_append(list, v); } bool json_list_append_bool(struct JsonValue *list, bool val) { struct JsonValue *v; v = json_new_bool(get_context(list), val); return json_list_append(list, v); } bool json_list_append_int(struct JsonValue *list, int64_t val) { struct JsonValue *v; v = json_new_int(get_context(list), val); return json_list_append(list, v); } bool json_list_append_float(struct JsonValue *list, double val) { struct JsonValue *v; v = json_new_float(get_context(list), val); return json_list_append(list, v); } bool json_list_append_string(struct JsonValue *list, const char *val) { struct JsonValue *v; v = json_new_string(get_context(list), val); return json_list_append(list, v); } bool json_dict_put(struct JsonValue *dict, const char *key, struct JsonValue *val) { struct JsonValue *kjv; struct JsonContainer *c; if (!key || !val) return false; if (!has_type(dict, JSON_DICT)) return false; if (!is_unattached(val)) return false; c = get_container(dict); kjv = json_new_string(c->c_ctx, key); if (!kjv) return false; if (!real_dict_add_key(c->c_ctx, dict, kjv)) return false; set_next(kjv, val); set_next(val, NULL); set_parent(val, dict); return true; } bool json_dict_put_null(struct JsonValue *dict, const char *key) { struct JsonValue *v; v = json_new_null(get_context(dict)); return json_dict_put(dict, key, v); } bool json_dict_put_bool(struct JsonValue *dict, const char *key, bool val) { struct JsonValue *v; v = json_new_bool(get_context(dict), val); return json_dict_put(dict, key, v); } bool json_dict_put_int(struct JsonValue *dict, const char *key, int64_t val) { struct JsonValue *v; v = json_new_int(get_context(dict), val); return json_dict_put(dict, key, v); } bool json_dict_put_float(struct JsonValue *dict, const char *key, double val) { struct JsonValue *v; v = json_new_float(get_context(dict), val); return json_dict_put(dict, key, v); } bool json_dict_put_string(struct JsonValue *dict, const char *key, const char *val) { struct JsonValue *v; v = json_new_string(get_context(dict), val); return json_dict_put(dict, key, v); } /* * Main context management */ struct JsonContext *json_new_context(const void *cx, size_t initial_mem) { struct JsonContext *ctx; CxMem *pool; pool = cx_new_pool(cx, initial_mem, 8); if (!pool) return NULL; ctx = cx_alloc0(pool, sizeof(*ctx)); if (!ctx) { cx_destroy(pool); return NULL; } ctx->pool = pool; return ctx; } void json_free_context(struct JsonContext *ctx) { if (ctx) { CxMem *pool = ctx->pool; memset(ctx, 0, sizeof(*ctx)); cx_destroy(pool); } } const char *json_strerror(struct JsonContext *ctx) { return ctx->lasterr; } void json_set_options(struct JsonContext *ctx, unsigned int options) { ctx->options = options; } pgbouncer-1.7/lib/usual/wchar.c0000664000175000017500000000652212511203511013433 00000000000000/* * wchar utility functions. * * Copyright (c) 2012 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include wchar_t *mbstr_decode(const char *str, int str_len, int *wlen_p, wchar_t *wbuf, int wbuf_len, bool allow_invalid) { mbstate_t ps; int clen; wchar_t *dst, *w, *wend; const char *s; const char *str_end; int wmax; if (str_len < 0) str_len = strlen(str); str_end = str + str_len; /* max number of wchar_t that the output can take plus zero-terminator */ wmax = str_len + 1; if (wbuf != NULL && wmax < wbuf_len) { dst = wbuf; } else { dst = malloc(sizeof(wchar_t) * wmax); if (!dst) return NULL; } /* try full decode at once */ s = str; memset(&ps, 0, sizeof(ps)); clen = mbsnrtowcs(dst, &s, str_len, wmax, &ps); if (clen >= 0) { if (wlen_p) *wlen_p = clen; dst[clen] = 0; return dst; } if (!allow_invalid) goto fail; /* full decode failed, decode chars one-by-one */ s = str; w = dst; wend = dst + wmax - 1; memset(&ps, 0, sizeof(ps)); while (s < str_end && w < wend) { clen = mbrtowc(w, s, str_end - s, &ps); if (clen > 0) { /* single char */ w++; s += clen; } else if (clen == 0) { /* string end */ break; } else if (allow_invalid) { /* allow invalid encoding */ memset(&ps, 0, sizeof(ps)); *w++ = (unsigned char)*s++; } else { /* invalid encoding */ goto fail; } } /* make sure we got string end */ if (s < str_end && *s != '\0') goto fail; *w = 0; if (wlen_p) *wlen_p = w - dst; return dst; fail: if (dst != wbuf) free(dst); return NULL; } wctype_t wctype_wcsn(const wchar_t *name, unsigned int namelen) { char buf[10]; unsigned int i; if (namelen >= sizeof(buf)) return (wctype_t)0; for (i = 0; i < namelen; i++) { wchar_t c = name[i]; if (c < 0x20 || c > 127) return (wctype_t)0; buf[i] = c; } buf[i] = 0; return wctype(buf); } #ifndef HAVE_MBSNRTOWCS size_t mbsnrtowcs(wchar_t *dst, const char **src_p, size_t srclen, size_t dstlen, mbstate_t *ps) { int clen; const char *s, *s_end; wchar_t *w; mbstate_t pstmp; size_t count = 0; if (!ps) { memset(&pstmp, 0, sizeof(pstmp)); ps = &pstmp; } s = *src_p; s_end = s + srclen; w = dst; while (s < s_end) { if (w && count >= dstlen) { /* dst is full */ break; } clen = mbrtowc(w, s, s_end - s, ps); if (clen > 0) { /* proper character */ if (w) w++; count++; s += clen; } else if (clen < 0) { /* invalid encoding */ *src_p = s; return (size_t)(-1); } else { /* end of string */ if (w) *w = 0; *src_p = NULL; return count; } } /* end due to srclen */ *src_p = s; return count; } #endif pgbouncer-1.7/lib/usual/endian.h0000664000175000017500000002133212511203511013566 00000000000000/* * Copyright (c) 2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Endianess conversion, convert integers to bytes. */ #ifndef _USUAL_ENDIAN_H_ #define _USUAL_ENDIAN_H_ #include #include /* * Need to include OS headers even if unused, so our * definitions stay in use. */ #ifdef HAVE_ENDIAN_H #include #endif #ifdef HAVE_SYS_ENDIAN_H #include #endif #ifdef HAVE_BYTESWAP_H #include #endif /* * Is unaligned access to integers OK? Does not apply to floats. * * OK: x86, amd64, arm >= v6, ppc */ #if defined(__amd64__) || defined(__i386__) || defined(__ppc__) || defined(__ppc64__) \ || defined(__ARM_FEATURE_UNALIGNED) \ || defined(_M_IX86) || defined(_M_X64) || defined(_M_PPC) \ || (defined(_M_ARM) && _M_ARM >= 6) #define WORDS_UNALIGNED_ACCESS_OK #endif /* * Ignore OS defines, as they may define only some subset of functions. * * Instead try to use compiler builtins. */ #undef bswap16 #undef bswap32 #undef bswap64 #undef htobe16 #undef htobe32 #undef htobe64 #undef htole16 #undef htole32 #undef htole64 #undef be16toh #undef be32toh #undef be64toh #undef le16toh #undef le32toh #undef le64toh #undef be16dec #undef be32dec #undef be64dec #undef le16dec #undef le32dec #undef le64dec #undef h16dec #undef h32dec #undef h64dec #undef be16enc #undef be32enc #undef be64enc #undef le16enc #undef le32enc #undef le64enc #undef h16enc #undef h32enc #undef h64enc /* * Redefine to avoid conflicts. */ #define bswap16(x) usual_bswap16(x) #define bswap32(x) usual_bswap32(x) #define bswap64(x) usual_bswap64(x) #define be16dec(p) usual_be16dec(p) #define be32dec(p) usual_be32dec(p) #define be64dec(p) usual_be64dec(p) #define le16dec(p) usual_le16dec(p) #define le32dec(p) usual_le32dec(p) #define le64dec(p) usual_le64dec(p) #define h16dec(p) usual_h16dec(p) #define h32dec(p) usual_h32dec(p) #define h64dec(p) usual_h64dec(p) #define be16enc(p, x) usual_be16enc(p, x) #define be32enc(p, x) usual_be32enc(p, x) #define be64enc(p, x) usual_be64enc(p, x) #define le16enc(p, x) usual_le16enc(p, x) #define le32enc(p, x) usual_le32enc(p, x) #define le64enc(p, x) usual_le64enc(p, x) #define h16enc(p, x) usual_h16enc(p, x) #define h32enc(p, x) usual_h32enc(p, x) #define h64enc(p, x) usual_h64enc(p, x) /** * @name Always swap. * * @{ */ /** Swap 16-bit int */ static inline uint16_t bswap16(uint16_t x) { #if _COMPILER_GNUC(4, 8) || __has_builtin(__builtin_bswap16) return __builtin_bswap16(x); #else return (x << 8) | (x >> 8); #endif } /** Swap 32-bit int */ static inline uint32_t bswap32(uint32_t x) { #if _COMPILER_GNUC(4, 3) || __has_builtin(__builtin_bswap32) return __builtin_bswap32(x); #else x = ((x << 8) & 0xFF00FF00) | ((x >> 8) & 0x00FF00FF); return (x << 16) | (x >> 16); #endif } /** Swap 64-bit int */ static inline uint64_t bswap64(uint64_t x) { #if _COMPILER_GNUC(4, 3) || __has_builtin(__builtin_bswap64) return __builtin_bswap64(x); #else return ((uint64_t)bswap32(x) << 32) | bswap32(x >> 32); #endif } /** * @} * * @name Convert host-endian int to BE/LE. * * @{ */ #ifdef WORDS_BIGENDIAN /** Convert native 16-bit int to big-endian */ #define htobe16(x) ((uint16_t)(x)) /** Convert native 32-bit int to big-endian */ #define htobe32(x) ((uint32_t)(x)) /** Convert native 64-bit int to big-endian */ #define htobe64(x) ((uint64_t)(x)) /** Convert native 16-bit int to little-endian */ #define htole16(x) bswap16(x) /** Convert native 32-bit int to little-endian */ #define htole32(x) bswap32(x) /** Convert native 64-bit int to little-endian */ #define htole64(x) bswap64(x) /** Convert big-endian 16-bit int to host-endian */ #define be16toh(x) ((uint16_t)(x)) /** Convert big-endian 32-bit int to host-endian */ #define be32toh(x) ((uint32_t)(x)) /** Convert big-endian 64-bit int to host-endian */ #define be64toh(x) ((uint64_t)(x)) /** Convert little-endian 16-bit int to host-endian */ #define le16toh(x) bswap16(x) /** Convert little-endian 32-bit int to host-endian */ #define le32toh(x) bswap32(x) /** Convert little-endian 64-bit int to host-endian */ #define le64toh(x) bswap64(x) #else /* !WORDS_BIGENDIAN */ /** Convert native 16-bit int to big-endian */ #define htobe16(x) bswap16(x) /** Convert native 32-bit int to big-endian */ #define htobe32(x) bswap32(x) /** Convert native 64-bit int to big-endian */ #define htobe64(x) bswap64(x) /** Convert native 16-bit int to little-endian */ #define htole16(x) ((uint16_t)(x)) /** Convert native 32-bit int to little-endian */ #define htole32(x) ((uint32_t)(x)) /** Convert native 64-bit int to little-endian */ #define htole64(x) ((uint64_t)(x)) /** Convert big-endian 16-bit int to host-endian */ #define be16toh(x) bswap16(x) /** Convert big-endian 32-bit int to host-endian */ #define be32toh(x) bswap32(x) /** Convert big-endian 64-bit int to host-endian */ #define be64toh(x) bswap64(x) /** Convert little-endian 64-bit int to host-endian */ #define le16toh(x) ((uint16_t)(x)) /** Convert little-endian 64-bit int to host-endian */ #define le32toh(x) ((uint32_t)(x)) /** Convert little-endian 64-bit int to host-endian */ #define le64toh(x) ((uint64_t)(x)) #endif /** * @} * * @name Read integer values from memory and convert to host format. * * @{ */ /** Read big-endian 16-bit int from memory */ static inline uint16_t be16dec(const void *p) { uint16_t tmp; memcpy(&tmp, p, sizeof(tmp)); return htobe16(tmp); } /** Read big-endian 32-bit int from memory */ static inline uint32_t be32dec(const void *p) { uint32_t tmp; memcpy(&tmp, p, sizeof(tmp)); return htobe32(tmp); } /** Read big-endian 64-bit int from memory */ static inline uint64_t be64dec(const void *p) { uint64_t tmp; memcpy(&tmp, p, sizeof(tmp)); return htobe64(tmp); } /** Read little-endian 16-bit int from memory */ static inline uint16_t le16dec(const void *p) { uint16_t tmp; memcpy(&tmp, p, sizeof(tmp)); return htole16(tmp); } /** Read little-endian 32-bit int from memory */ static inline uint32_t le32dec(const void *p) { uint32_t tmp; memcpy(&tmp, p, sizeof(tmp)); return htole32(tmp); } /** Read little-endian 64-bit int from memory */ static inline uint64_t le64dec(const void *p) { uint64_t tmp; memcpy(&tmp, p, sizeof(tmp)); return htole64(tmp); } /** Read host-endian 16-bit int from memory */ static inline uint16_t h16dec(const void *p) { uint16_t tmp; memcpy(&tmp, p, sizeof(tmp)); return tmp; } /** Read host-endian 32-bit int from memory */ static inline uint32_t h32dec(const void *p) { uint32_t tmp; memcpy(&tmp, p, sizeof(tmp)); return tmp; } /** Read host-endian 64-bit int from memory */ static inline uint64_t h64dec(const void *p) { uint64_t tmp; memcpy(&tmp, p, sizeof(tmp)); return tmp; } /** * @} * * @name Convert host value to LE/BE and write to memory * * @{ */ /** Write big-endian 16-bit int to memory */ static inline void be16enc(void *p, uint16_t x) { uint16_t tmp = htobe16(x); memcpy(p, &tmp, sizeof(tmp)); } /** Write big-endian 32-bit int to memory */ static inline void be32enc(void *p, uint32_t x) { uint32_t tmp = htobe32(x); memcpy(p, &tmp, sizeof(tmp)); } /** Write big-endian 64-bit int to memory */ static inline void be64enc(void *p, uint64_t x) { uint64_t tmp = htobe64(x); memcpy(p, &tmp, sizeof(tmp)); } /** Write little-endian 16-bit int to memory */ static inline void le16enc(void *p, uint16_t x) { uint16_t tmp = htole16(x); memcpy(p, &tmp, sizeof(tmp)); } /** Write little-endian 32-bit int to memory */ static inline void le32enc(void *p, uint32_t x) { uint32_t tmp = htole32(x); memcpy(p, &tmp, sizeof(tmp)); } /** Write little-endian 64-bit int to memory */ static inline void le64enc(void *p, uint64_t x) { uint64_t tmp = htole64(x); memcpy(p, &tmp, sizeof(tmp)); } /** Write host-endian 16-bit int to memory */ static inline void h16enc(void *p, uint16_t x) { memcpy(p, &x, sizeof(x)); } /** Write host-endian 32-bit int to memory */ static inline void h32enc(void *p, uint32_t x) { memcpy(p, &x, sizeof(x)); } /** Write host-endian 64-bit int to memory */ static inline void h64enc(void *p, uint64_t x) { memcpy(p, &x, sizeof(x)); } /** @} */ #endif /* _USUAL_ENDIAN_H_ */ pgbouncer-1.7/lib/usual/pthread.c0000664000175000017500000000460512511203511013756 00000000000000/* * Pthreads compat. * * Copyright (c) 2007-2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #ifndef HAVE_PTHREAD_H #ifdef WIN32 /* * basic pthreads for win32. */ struct _w32thread { void *(*fn)(void *); void *arg; }; static DWORD WINAPI w32launcher(LPVOID arg) { struct _w32thread *info = arg; info->fn(info->arg); free(info); return 0; } int pthread_create(pthread_t *t, pthread_attr_t *attr, void *(*fn)(void *), void *arg) { struct _w32thread *info = calloc(1, sizeof(*info)); if (!info) return -1; info->fn = fn; info->arg = arg; *t = CreateThread(NULL, 0, w32launcher, info, 0, NULL); if (*t == NULL) return -1; return 0; } int pthread_join(pthread_t *t, void **ret) { if (WaitForSingleObject(*t, INFINITE) != WAIT_OBJECT_0) return -1; CloseHandle(*t); return 0; } int pthread_mutex_init(pthread_mutex_t *lock, void *unused) { *lock = CreateMutex(NULL, FALSE, NULL); if (*lock == NULL) return -1; return 0; } int pthread_mutex_destroy(pthread_mutex_t *lock) { if (*lock) { CloseHandle(*lock); *lock = NULL; } return 0; } int pthread_mutex_lock(pthread_mutex_t *lock) { if (WaitForSingleObject(*lock, INFINITE) != WAIT_OBJECT_0) return -1; return 0; } int pthread_mutex_unlock(pthread_mutex_t *lock) { if (!ReleaseMutex(*lock)) return -1; return 0; } #ifdef INIT_ONCE_STATIC_INIT typedef void (*once_posix_cb_t)(void); static BOOL once_wrapper(PINIT_ONCE once, void *arg, void **ctx) { once_posix_cb_t cb = arg; arg(); return TRUE; } int pthread_once(pthread_once_t *once, void (*once_func)(void)) { return InitOnceExecuteOnce(once, once_wrapper, once_func, NULL) ? 0 : -1; } #endif #endif /* win32 */ #endif /* !HAVE_PTHREAD_H */ pgbouncer-1.7/lib/usual/time.c0000664000175000017500000001117512620435335013301 00000000000000/* * Common time functions. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include char *format_time_ms(usec_t time, char *dst, unsigned dstlen) { struct tm *tm, tmbuf; struct timeval tv; time_t sec; if (!time) { gettimeofday(&tv, NULL); } else { tv.tv_sec = time / USEC; tv.tv_usec = time % USEC; } sec = tv.tv_sec; tm = localtime_r(&sec, &tmbuf); snprintf(dst, dstlen, "%04d-%02d-%02d %02d:%02d:%02d.%03d", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, (int)(tv.tv_usec / 1000)); return dst; } char *format_time_s(usec_t time, char *dst, unsigned dstlen) { time_t s; struct tm tbuf, *tm; if (!time) { struct timeval tv; gettimeofday(&tv, NULL); s = tv.tv_sec; } else { s = time / USEC; } tm = localtime_r(&s, &tbuf); snprintf(dst, dstlen, "%04d-%02d-%02d %02d:%02d:%02d", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); return dst; } /* read current time */ usec_t get_time_usec(void) { struct timeval tv; gettimeofday(&tv, NULL); return (usec_t)tv.tv_sec * USEC + tv.tv_usec; } static usec_t _time_cache; /* read cached time */ usec_t get_cached_time(void) { if (!_time_cache) _time_cache = get_time_usec(); return _time_cache; } /* forget cached time, let next read fill it */ void reset_time_cache(void) { _time_cache = 0; } /* * win32 compat */ #ifdef WIN32 /* unix epoch (1970) in seconds from windows epoch (1601) */ #define UNIX_EPOCH 11644473600LL /* 1 sec in 100 nsec units */ #define FT_SEC 10000000LL static void ft2tv(FILETIME *src, struct timeval *dst, bool use_epoch) { ULARGE_INTEGER tmp; tmp.LowPart = src->dwLowDateTime; tmp.HighPart = src->dwHighDateTime; dst->tv_sec = (tmp.QuadPart / FT_SEC) - (use_epoch ? UNIX_EPOCH : 0); dst->tv_usec = (tmp.QuadPart % FT_SEC) / 10; } #ifndef HAVE_GETTIMEOFDAY int gettimeofday(struct timeval * tp, void * tzp) { FILETIME file_time; SYSTEMTIME system_time; /* read UTC timestamp */ GetSystemTime(&system_time); SystemTimeToFileTime(&system_time, &file_time); /* convert to timeval */ ft2tv(&file_time, tp, true); return 0; } #endif /* !HAVE_GETTIMEOFDAY */ #ifndef HAVE_LOCALTIME_R struct tm *localtime_r(const time_t *tp, struct tm *dst) { ULARGE_INTEGER utc; FILETIME ft_utc; SYSTEMTIME st_utc, st_local; /* convert time_t to FILETIME */ utc.QuadPart = (*tp + UNIX_EPOCH) * FT_SEC; ft_utc.dwLowDateTime = utc.LowPart; ft_utc.dwHighDateTime = utc.HighPart; /* split to parts and get local time */ if (!FileTimeToSystemTime(&ft_utc, &st_utc)) return NULL; if (!SystemTimeToTzSpecificLocalTime(NULL, &st_utc, &st_local)) return NULL; /* fill struct tm */ dst->tm_sec = st_local.wSecond; dst->tm_min = st_local.wMinute; dst->tm_hour = st_local.wHour; dst->tm_mday = st_local.wDay; dst->tm_mon = st_local.wMonth - 1; dst->tm_year = st_local.wYear - 1900; dst->tm_wday = st_local.wDayOfWeek; dst->tm_yday = 0; dst->tm_isdst = -1; return dst; } #endif /* !HAVE_LOCALTIME_R */ #ifndef HAVE_GETRUSAGE int getrusage(int who, struct rusage *dst) { FILETIME tcreate, texit, tkern, tuser; if (who != RUSAGE_SELF) { errno = EINVAL; return -1; } if (!GetProcessTimes(GetCurrentProcess(), &tcreate, &texit, &tkern, &tuser)) return -1; ft2tv(&tuser, &dst->ru_utime, false); ft2tv(&tkern, &dst->ru_stime, false); return 0; } #endif /* !HAVE_GETRUSAGE */ #ifndef HAVE_TIMEGM time_t timegm(struct tm *tm) { #ifdef WIN32 return _mkgmtime(tm); #else char buf[128], *tz, *old = NULL; time_t secs; tz = getenv("TZ"); if (tz) { old = strdup(tz); if (!old) { strlcpy(buf, tz, sizeof buf); old = buf; } } setenv("TZ", "", 1); tzset(); secs = mktime(tm); if (old) { setenv("TZ", old, 1); } else { unsetenv("TZ"); } tzset(); if (old && old != buf) free(old); return secs; #endif } #endif /* HAVE_TIMEGM */ #endif /* WIN32 */ pgbouncer-1.7/lib/usual/socket_pton.c0000664000175000017500000001224012511202014014646 00000000000000/* $OpenBSD: inet_pton.c,v 1.8 2010/05/06 15:47:14 claudio Exp $ */ /* Copyright (c) 1996 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #include #include #ifndef HAVE_INET_PTON #ifndef INADDRSZ #define INADDRSZ 4 #endif #ifndef IN6ADDRSZ #define IN6ADDRSZ 16 #endif #ifndef INT16SZ #define INT16SZ 2 #endif #define u_char uint8_t #define u_int unsigned int /* * WARNING: Don't even consider trying to compile this on a system where * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. */ static int inet_pton4(const char *src, u_char *dst); static int inet_pton6(const char *src, u_char *dst); /* int * inet_pton(af, src, dst) * convert from presentation format (which usually means ASCII printable) * to network format (which is usually some kind of binary format). * return: * 1 if the address was valid for the specified address family * 0 if the address wasn't valid (`dst' is untouched in this case) * -1 if some other error occurred (`dst' is untouched in this case, too) * author: * Paul Vixie, 1996. */ int inet_pton(int af, const char *src, void *dst) { switch (af) { case AF_INET: return (inet_pton4(src, dst)); case AF_INET6: return (inet_pton6(src, dst)); default: errno = EAFNOSUPPORT; return (-1); } /* NOTREACHED */ } /* int * inet_pton4(src, dst) * like inet_aton() but without all the hexadecimal and shorthand. * return: * 1 if `src' is a valid dotted quad, else 0. * notice: * does not touch `dst' unless it's returning 1. * author: * Paul Vixie, 1996. */ static int inet_pton4(const char *src, u_char *dst) { static const char digits[] = "0123456789"; int saw_digit, octets, ch; u_char tmp[INADDRSZ], *tp; saw_digit = 0; octets = 0; *(tp = tmp) = 0; while ((ch = *src++) != '\0') { const char *pch; if ((pch = strchr(digits, ch)) != NULL) { u_int new = *tp * 10 + (pch - digits); if (new > 255) return (0); if (! saw_digit) { if (++octets > 4) return (0); saw_digit = 1; } *tp = new; } else if (ch == '.' && saw_digit) { if (octets == 4) return (0); *++tp = 0; saw_digit = 0; } else return (0); } if (octets < 4) return (0); memcpy(dst, tmp, INADDRSZ); return (1); } /* int * inet_pton6(src, dst) * convert presentation level address to network order binary form. * return: * 1 if `src' is a valid [RFC1884 2.2] address, else 0. * notice: * does not touch `dst' unless it's returning 1. * credit: * inspired by Mark Andrews. * author: * Paul Vixie, 1996. */ static int inet_pton6(const char *src, u_char *dst) { static const char xdigits_l[] = "0123456789abcdef", xdigits_u[] = "0123456789ABCDEF"; u_char tmp[IN6ADDRSZ], *tp, *endp, *colonp; const char *xdigits, *curtok; int ch, saw_xdigit, count_xdigit; u_int val; memset((tp = tmp), '\0', IN6ADDRSZ); endp = tp + IN6ADDRSZ; colonp = NULL; /* Leading :: requires some special handling. */ if (*src == ':') if (*++src != ':') return (0); curtok = src; saw_xdigit = count_xdigit = 0; val = 0; while ((ch = *src++) != '\0') { const char *pch; if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL) pch = strchr((xdigits = xdigits_u), ch); if (pch != NULL) { if (count_xdigit >= 4) return (0); val <<= 4; val |= (pch - xdigits); if (val > 0xffff) return (0); saw_xdigit = 1; count_xdigit++; continue; } if (ch == ':') { curtok = src; if (!saw_xdigit) { if (colonp) return (0); colonp = tp; continue; } else if (*src == '\0') { return (0); } if (tp + INT16SZ > endp) return (0); *tp++ = (u_char) (val >> 8) & 0xff; *tp++ = (u_char) val & 0xff; saw_xdigit = 0; count_xdigit = 0; val = 0; continue; } if (ch == '.' && ((tp + INADDRSZ) <= endp) && inet_pton4(curtok, tp) > 0) { tp += INADDRSZ; saw_xdigit = 0; count_xdigit = 0; break; /* '\0' was seen by inet_pton4(). */ } return (0); } if (saw_xdigit) { if (tp + INT16SZ > endp) return (0); *tp++ = (u_char) (val >> 8) & 0xff; *tp++ = (u_char) val & 0xff; } if (colonp != NULL) { /* * Since some memmove()'s erroneously fail to handle * overlapping regions, we'll do the shift by hand. */ const int n = tp - colonp; int i; if (tp == endp) return (0); for (i = 1; i <= n; i++) { endp[- i] = colonp[n - i]; colonp[n - i] = 0; } tp = endp; } if (tp != endp) return (0); memcpy(dst, tmp, IN6ADDRSZ); return (1); } #endif pgbouncer-1.7/lib/usual/tls/0000775000175000017500000000000012635051616013056 500000000000000pgbouncer-1.7/lib/usual/tls/tls.h0000664000175000017500000001466612615704017013764 00000000000000/* $OpenBSD: tls.h,v 1.12 2015/03/31 14:03:38 jsing Exp $ */ /* * Copyright (c) 2014 Joel Sing * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _USUAL_HEADER_TLS_H_ #define _USUAL_HEADER_TLS_H_ #ifdef __cplusplus extern "C" { #endif #include #define TLS_API 20141031 #define TLS_PROTOCOL_TLSv1_0 (1 << 1) #define TLS_PROTOCOL_TLSv1_1 (1 << 2) #define TLS_PROTOCOL_TLSv1_2 (1 << 3) #define TLS_PROTOCOL_TLSv1 \ (TLS_PROTOCOL_TLSv1_0|TLS_PROTOCOL_TLSv1_1|TLS_PROTOCOL_TLSv1_2) #define TLS_PROTOCOLS_ALL TLS_PROTOCOL_TLSv1 #define TLS_PROTOCOLS_DEFAULT TLS_PROTOCOL_TLSv1_2 #define TLS_WANT_POLLIN -2 #define TLS_WANT_POLLOUT -3 #define TLS_NO_OCSP -4 #define TLS_NO_CERT -5 #define TLS_OCSP_RESPONSE_SUCCESSFUL 0 #define TLS_OCSP_RESPONSE_MALFORMED 1 #define TLS_OCSP_RESPONSE_INTERNALERR 2 #define TLS_OCSP_RESPONSE_TRYLATER 3 #define TLS_OCSP_RESPONSE_SIGREQUIRED 5 #define TLS_OCSP_RESPONSE_UNAUTHORIZED 6 #define TLS_OCSP_CERT_GOOD 0 #define TLS_OCSP_CERT_REVOKED 1 #define TLS_OCSP_CERT_UNKNOWN 2 #define TLS_CRL_REASON_UNPSECIFIED 0 #define TLS_CRL_REASON_KEY_COMPROMISE 1 #define TLS_CRL_REASON_CA_COMPROMISE 2 #define TLS_CRL_REASON_AFFILIATION_CHANGED 3 #define TLS_CRL_REASON_SUPERSEDED 4 #define TLS_CRL_REASON_CESSATION_OF_OPERATION 5 #define TLS_CRL_REASON_CERTIFICATE_HOLD 6 #define TLS_CRL_REASON_REMOVE_FROM_CRL 8 #define TLS_CRL_REASON_PRIVILEGE_WITH_DRAWN 9 #define TLS_CRL_REASON_AA_COMPROMISE 10 struct tls; struct tls_config; int tls_init(void); const char *tls_error(struct tls *_ctx); struct tls_config *tls_config_new(void); void tls_config_free(struct tls_config *_config); int tls_config_set_ca_file(struct tls_config *_config, const char *_ca_file); int tls_config_set_ca_path(struct tls_config *_config, const char *_ca_path); int tls_config_set_ca_mem(struct tls_config *_config, const uint8_t *_ca, size_t _len); int tls_config_set_cert_file(struct tls_config *_config, const char *_cert_file); int tls_config_set_cert_mem(struct tls_config *_config, const uint8_t *_cert, size_t _len); int tls_config_set_ciphers(struct tls_config *_config, const char *_ciphers); int tls_config_set_dheparams(struct tls_config *_config, const char *_params); int tls_config_set_ecdhecurve(struct tls_config *_config, const char *_name); int tls_config_set_key_file(struct tls_config *_config, const char *_key_file); int tls_config_set_key_mem(struct tls_config *_config, const uint8_t *_key, size_t _len); int tls_config_set_ocsp_stapling_file(struct tls_config *_config, const char *_blob_file); int tls_config_set_ocsp_stapling_mem(struct tls_config *_config, const uint8_t *_blob, size_t _len); void tls_config_set_protocols(struct tls_config *_config, uint32_t _protocols); void tls_config_set_verify_depth(struct tls_config *_config, int _verify_depth); void tls_config_prefer_ciphers_client(struct tls_config *_config); void tls_config_prefer_ciphers_server(struct tls_config *_config); void tls_config_insecure_noverifycert(struct tls_config *_config); void tls_config_insecure_noverifyname(struct tls_config *_config); void tls_config_insecure_noverifytime(struct tls_config *_config); void tls_config_verify(struct tls_config *_config); void tls_config_verify_client(struct tls_config *_config); void tls_config_verify_client_optional(struct tls_config *_config); void tls_config_clear_keys(struct tls_config *_config); int tls_config_parse_protocols(uint32_t *_protocols, const char *_protostr); struct tls *tls_client(void); struct tls *tls_server(void); int tls_configure(struct tls *_ctx, struct tls_config *_config); void tls_reset(struct tls *_ctx); void tls_free(struct tls *_ctx); int tls_accept_fds(struct tls *_ctx, struct tls **_cctx, int _fd_read, int _fd_write); int tls_accept_socket(struct tls *_ctx, struct tls **_cctx, int _socket); int tls_connect(struct tls *_ctx, const char *_host, const char *_port); int tls_connect_fds(struct tls *_ctx, int _fd_read, int _fd_write, const char *_servername); int tls_connect_servername(struct tls *_ctx, const char *_host, const char *_port, const char *_servername); int tls_connect_socket(struct tls *_ctx, int _s, const char *_servername); int tls_handshake(struct tls *_ctx); ssize_t tls_read(struct tls *_ctx, void *_buf, size_t _buflen); ssize_t tls_write(struct tls *_ctx, const void *_buf, size_t _buflen); int tls_close(struct tls *_ctx); int tls_peer_cert_provided(struct tls *ctx); int tls_peer_cert_contains_name(struct tls *ctx, const char *name); const char * tls_peer_cert_hash(struct tls *_ctx); const char * tls_peer_cert_issuer(struct tls *ctx); const char * tls_peer_cert_subject(struct tls *ctx); time_t tls_peer_cert_notbefore(struct tls *ctx); time_t tls_peer_cert_notafter(struct tls *ctx); const char * tls_conn_version(struct tls *ctx); const char * tls_conn_cipher(struct tls *ctx); uint8_t *tls_load_file(const char *_file, size_t *_len, char *_password); ssize_t tls_get_connection_info(struct tls *ctx, char *buf, size_t buflen); int tls_ocsp_refresh_stapling(struct tls **ocsp_ctx_p, int *async_fd_p, struct tls_config *config); int tls_ocsp_check_peer(struct tls **ocsp_ctx_p, int *async_fd_p, struct tls *client); int tls_get_ocsp_info(struct tls *ctx, int *response_status, int *cert_status, int *crl_reason, time_t *this_update, time_t *next_update, time_t *revoction_time, const char **result_text); int tls_ocsp_check_peer_request(struct tls **ocsp_ctx_p, struct tls *target, char **ocsp_url, void **request_blob, size_t *request_size); int tls_ocsp_refresh_stapling_request(struct tls **ocsp_ctx_p, struct tls_config *config, char **ocsp_url, void **request_blob, size_t *request_size); int tls_ocsp_process_response(struct tls *ctx, const void *response_blob, size_t size); #ifdef __cplusplus } #endif #endif /* HEADER_TLS_H */ pgbouncer-1.7/lib/usual/tls/tls_internal.h0000664000175000017500000001165412615704017015652 00000000000000/* $OpenBSD: tls_internal.h,v 1.11 2015/02/22 14:50:41 jsing Exp $ */ /* * Copyright (c) 2014 Jeremie Courreges-Anglas * Copyright (c) 2014 Joel Sing * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef HEADER_TLS_INTERNAL_H #define HEADER_TLS_INTERNAL_H #include #include #include #define _PATH_SSL_CA_FILE USUAL_TLS_CA_FILE /* * Anything that is not completely broken. * * Also fixes 3DES ordering bug in older OpenSSLs. */ #define TLS_CIPHERS_COMPAT "HIGH:+3DES:!aNULL" /* * All==insecure. */ #define TLS_CIPHERS_ALL "ALL:!aNULL:!eNULL" /* * TLSv1.2+AEAD+ECDHE/DHE. CBC modes are dubious due to spec bugs in TLS. */ #define TLS_CIPHERS_DEFAULT "HIGH+EECDH:HIGH+EDH:!SSLv3:!SHA384:!SHA256:!DSS:!aNULL" /* * Compact subset of reasonable suites only. * * Priorities, in order: * - ECDHE > DHE > RSA * - AESGCM > CBC * - TLSv1.2 > TLSv1.0 * - AES256 > AES128. */ #define TLS_CIPHERS_NORMAL "HIGH+EECDH:HIGH+EDH:HIGH+RSA:+SHA384:+SHA256:+SSLv3:+EDH:+RSA:-3DES:3DES+RSA:!CAMELLIA:!DSS:!aNULL" /* * Prefer performance if it does not affect security. * * Same as "normal" but prefers AES128 to AES256. */ #define TLS_CIPHERS_FAST "HIGH+EECDH:HIGH+EDH:HIGH+RSA:+AES256:+SHA256:+SHA384:+SSLv3:+EDH:+RSA:-3DES:3DES+RSA:!CAMELLIA:!DSS:!aNULL" union tls_addr { struct in_addr ip4; struct in6_addr ip6; }; struct tls_config { const char *ca_file; const char *ca_path; char *ca_mem; size_t ca_len; const char *cert_file; char *cert_mem; size_t cert_len; const char *ciphers; int ciphers_server; int dheparams; int ecdhecurve; const char *key_file; char *key_mem; size_t key_len; const char *ocsp_file; char *ocsp_mem; size_t ocsp_len; uint32_t protocols; int verify_cert; int verify_client; int verify_depth; int verify_name; int verify_time; }; struct tls_conninfo { char *issuer; char *subject; char *hash; char *serial; char *fingerprint; char *version; char *cipher; time_t notbefore; time_t notafter; }; #define TLS_CLIENT (1 << 0) #define TLS_SERVER (1 << 1) #define TLS_SERVER_CONN (1 << 2) #define TLS_OCSP_CLIENT (1 << 3) #define TLS_EOF_NO_CLOSE_NOTIFY (1 << 0) #define TLS_HANDSHAKE_COMPLETE (1 << 1) #define TLS_DO_ABORT (1 << 8) struct tls_ocsp_query; struct tls_ocsp_info; struct tls { struct tls_config *config; uint32_t flags; uint32_t state; char *errmsg; int errnum; char *servername; int socket; SSL *ssl_conn; SSL_CTX *ssl_ctx; X509 *ssl_peer_cert; struct tls_conninfo *conninfo; int used_dh_bits; int used_ecdh_nid; const char *ocsp_result; struct tls_ocsp_info *ocsp_info; struct tls_ocsp_query *ocsp_query; }; struct tls_ocsp_info { int response_status; int cert_status; int crl_reason; time_t this_update; time_t next_update; time_t revocation_time; }; struct tls *tls_new(void); struct tls *tls_server_conn(struct tls *ctx); int tls_check_name(struct tls *ctx, X509 *cert, const char *servername); int tls_configure_keypair(struct tls *ctx, int); int tls_configure_server(struct tls *ctx); int tls_configure_ssl(struct tls *ctx); int tls_configure_ssl_verify(struct tls *ctx, int verify); int tls_handshake_client(struct tls *ctx); int tls_handshake_server(struct tls *ctx); int tls_host_port(const char *hostport, char **host, char **port); int tls_set_error(struct tls *ctx, const char *fmt, ...) __attribute__((__format__ (printf, 2, 3))) __attribute__((__nonnull__ (2))); int tls_set_errorx(struct tls *ctx, const char *fmt, ...) __attribute__((__format__ (printf, 2, 3))) __attribute__((__nonnull__ (2))); int tls_set_error_libssl(struct tls *ctx, const char *fmt, ...) __attribute__((__format__ (printf, 2, 3))) __attribute__((__nonnull__ (2))); int tls_ssl_error(struct tls *ctx, SSL *ssl_conn, int ssl_ret, const char *prefix); int tls_get_conninfo(struct tls *ctx); void tls_free_conninfo(struct tls_conninfo *conninfo); int tls_ocsp_verify_callback(SSL *ssl, void *arg); int tls_ocsp_stapling_callback(SSL *ssl, void *arg); void tls_ocsp_client_free(struct tls *ctx); void tls_ocsp_info_free(struct tls_ocsp_info *info); int tls_asn1_parse_time(struct tls *ctx, const ASN1_TIME *asn1time, time_t *dst); int asn1_time_parse(const char *, size_t, struct tm *, int); #endif /* HEADER_TLS_INTERNAL_H */ pgbouncer-1.7/lib/usual/tls/tls_util.c0000664000175000017500000001176612615704017015012 00000000000000/* $OpenBSD: tls_util.c,v 1.1 2014/10/31 13:46:17 jsing Exp $ */ /* * Copyright (c) 2014 Joel Sing * Copyright (c) 2015 Reyk Floeter * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "tls_compat.h" #ifdef USUAL_LIBSSL_FOR_TLS #include #include #include #include "tls_internal.h" /* * Extract the host and port from a colon separated value. For a literal IPv6 * address the address must be contained with square braces. If a host and * port are successfully extracted, the function will return 0 and the * caller is responsible for freeing the host and port. If no port is found * then the function will return 1, with both host and port being NULL. * On memory allocation failure -1 will be returned. */ int tls_host_port(const char *hostport, char **host, char **port) { char *h, *p, *s; int rv = 1; *host = NULL; *port = NULL; if ((s = strdup(hostport)) == NULL) goto fail; h = p = s; /* See if this is an IPv6 literal with square braces. */ if (p[0] == '[') { h++; if ((p = strchr(s, ']')) == NULL) goto done; *p++ = '\0'; } /* Find the port seperator. */ if ((p = strchr(p, ':')) == NULL) goto done; /* If there is another separator then we have issues. */ if (strchr(p + 1, ':') != NULL) goto done; *p++ = '\0'; if (asprintf(host, "%s", h) == -1) goto fail; if (asprintf(port, "%s", p) == -1) goto fail; rv = 0; goto done; fail: free(*host); *host = NULL; free(*port); *port = NULL; rv = -1; done: free(s); return (rv); } static int tls_password_cb(char *buf, int size, int rwflag, void *u) { size_t len; if (u == NULL) { memset(buf, 0, size); return (0); } if ((len = strlcpy(buf, u, size)) >= (size_t)size) return (0); return (len); } uint8_t * tls_load_file(const char *name, size_t *len, char *password) { FILE *fp; EVP_PKEY *key = NULL; BIO *bio = NULL; uint8_t *buf = NULL; char *data; struct stat st; size_t size; int fd = -1; *len = 0; if ((fd = open(name, O_RDONLY)) == -1) return (NULL); /* Just load the file into memory without decryption */ if (password == NULL) { if (fstat(fd, &st) != 0) goto fail; size = (size_t)st.st_size; if ((buf = calloc(1, size + 1)) == NULL) goto fail; if (read(fd, buf, size) != (ssize_t)size) goto fail; close(fd); goto done; } /* Or read the (possibly) encrypted key from file */ if ((fp = fdopen(fd, "r")) == NULL) goto fail; fd = -1; key = PEM_read_PrivateKey(fp, NULL, tls_password_cb, password); fclose(fp); if (key == NULL) goto fail; /* Write unencrypted key to memory buffer */ if ((bio = BIO_new(BIO_s_mem())) == NULL) goto fail; if (!PEM_write_bio_PrivateKey(bio, key, NULL, NULL, 0, NULL, NULL)) goto fail; if ((size = BIO_get_mem_data(bio, &data)) <= 0) goto fail; if ((buf = calloc(1, size)) == NULL) goto fail; memcpy(buf, data, size); BIO_free_all(bio); EVP_PKEY_free(key); done: *len = size; return (buf); fail: free(buf); if (fd != -1) close(fd); if (bio != NULL) BIO_free_all(bio); if (key != NULL) EVP_PKEY_free(key); return (NULL); } ssize_t tls_get_connection_info(struct tls *ctx, char *buf, size_t buflen) { SSL *conn = ctx->ssl_conn; const char *ocsp_pfx = "", *ocsp_info = ""; const char *proto = "-", *cipher = "-"; char dh[64]; int used_dh_bits = ctx->used_dh_bits, used_ecdh_nid = ctx->used_ecdh_nid; if (conn != NULL) { proto = SSL_get_version(conn); cipher = SSL_get_cipher(conn); #ifdef SSL_get_server_tmp_key if (ctx->flags & TLS_CLIENT) { EVP_PKEY *pk = NULL; int ok = SSL_get_server_tmp_key(conn, &pk); if (ok && pk) { if (pk->type == EVP_PKEY_DH) { DH *dh = EVP_PKEY_get0(pk); used_dh_bits = DH_size(dh) * 8; } else if (pk->type == EVP_PKEY_EC) { EC_KEY *ecdh = EVP_PKEY_get0(pk); const EC_GROUP *eg = EC_KEY_get0_group(ecdh); used_ecdh_nid = EC_GROUP_get_curve_name(eg); } EVP_PKEY_free(pk); } } #endif } if (used_dh_bits) { snprintf(dh, sizeof dh, "/DH=%d", used_dh_bits); } else if (used_ecdh_nid) { snprintf(dh, sizeof dh, "/ECDH=%s", OBJ_nid2sn(used_ecdh_nid)); } else { dh[0] = 0; } if (ctx->ocsp_result) { ocsp_info = ctx->ocsp_result; ocsp_pfx = "/OCSP="; } return snprintf(buf, buflen, "%s/%s%s%s%s", proto, cipher, dh, ocsp_pfx, ocsp_info); } #endif /* USUAL_LIBSSL_FOR_TLS */ pgbouncer-1.7/lib/usual/tls/tls_cert.c0000664000175000017500000004077512576101475015001 00000000000000/* $OpenBSD: tls_verify.c,v 1.7 2015/02/11 06:46:33 jsing Exp $ */ /* * Copyright (c) 2014 Jeremie Courreges-Anglas * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "tls_compat.h" #ifdef USUAL_LIBSSL_FOR_TLS #include #include #include #include "tls_internal.h" #define TLS_CERT_INTERNAL_FUNCS #include "tls_cert.h" /* * Load cert data from X509 cert. */ /* Upper bounds */ #define UB_COMMON_NAME 255 #define UB_COUNTRY_NAME 255 #define UB_STATE_NAME 255 #define UB_LOCALITY_NAME 255 #define UB_STREET_ADDRESS 255 #define UB_ORGANIZATION_NAME 255 #define UB_ORGANIZATIONAL_UNIT_NAME 255 #define UB_GNAME_DNS 255 #define UB_GNAME_EMAIL 255 #define UB_GNAME_URI 255 /* Convert ASN1_INTEGER to decimal string string */ static int tls_parse_bigint(struct tls *ctx, const ASN1_INTEGER *asn1int, const char **dst_p) { long small; BIGNUM *big; char *tmp, buf[64]; *dst_p = NULL; small = ASN1_INTEGER_get(asn1int); if (small < 0) { big = ASN1_INTEGER_to_BN(asn1int, NULL); if (big) { tmp = BN_bn2dec(big); if (tmp) *dst_p = strdup(tmp); OPENSSL_free(tmp); } BN_free(big); } else { snprintf(buf, sizeof buf, "%lu", small); *dst_p = strdup(buf); } if (*dst_p) return 0; tls_set_errorx(ctx, "cannot parse serial"); return -1; } /* * Decode all string types used in RFC5280. * * OpenSSL used (before Jun 1 2014 commit) to pick between PrintableString, * T61String, BMPString and UTF8String, depending on data. This code * tries to match that. * * Disallow any ancient ASN.1 escape sequences. */ static int check_invalid_bytes(struct tls *ctx, unsigned char *data, unsigned int len, int ascii_only, const char *desc) { unsigned int i, c; /* data is utf8 string, check for crap */ for (i = 0; i < len; i++) { c = data[i]; if (ascii_only && (c & 0x80) != 0) { tls_set_errorx(ctx, "invalid %s: contains non-ascii in ascii string", desc); goto failed; } else if (c < 0x20) { /* ascii control chars, including NUL */ if (c != '\t' && c != '\n' && c != '\r') { tls_set_errorx(ctx, "invalid %s: contains C0 control char", desc); goto failed; } } else if (c == 0xC2 && (i + 1) < len) { /* C1 control chars in UTF-8: \xc2\x80 - \xc2\x9f */ c = data[i + 1]; if (c >= 0x80 && c <= 0x9F) { tls_set_errorx(ctx, "invalid %s: contains C1 control char", desc); goto failed; } } else if (c == 0x7F) { tls_set_errorx(ctx, "invalid %s: contains DEL char", desc); goto failed; } } return 0; failed: return -1; } static int tls_parse_asn1string(struct tls *ctx, ASN1_STRING *a1str, const char **dst_p, int minchars, int maxchars, const char *desc) { int format, len, ret = -1; unsigned char *data; ASN1_STRING *a1utf = NULL; int ascii_only = 0; char *cstr = NULL; int mbres, mbconvert = -1; *dst_p = NULL; format = ASN1_STRING_type(a1str); data = ASN1_STRING_data(a1str); len = ASN1_STRING_length(a1str); if (len < minchars) { tls_set_errorx(ctx, "invalid %s: string too short", desc); goto failed; } switch (format) { case V_ASN1_NUMERICSTRING: case V_ASN1_VISIBLESTRING: case V_ASN1_PRINTABLESTRING: case V_ASN1_IA5STRING: /* Ascii */ if (len > maxchars) { tls_set_errorx(ctx, "invalid %s: string too long", desc); goto failed; } ascii_only = 1; break; case V_ASN1_T61STRING: /* Latin1 */ mbconvert = MBSTRING_ASC; break; case V_ASN1_BMPSTRING: /* UCS-2 big-endian */ mbconvert = MBSTRING_BMP; break; case V_ASN1_UNIVERSALSTRING: /* UCS-4 big-endian */ mbconvert = MBSTRING_UNIV; break; case V_ASN1_UTF8STRING: /* * UTF-8 - could be used directly if OpenSSL has already * validated the data. ATM be safe and validate here. */ mbconvert = MBSTRING_UTF8; break; default: tls_set_errorx(ctx, "invalid %s: unexpected string type", desc); goto failed; } /* Convert to UTF-8 */ if (mbconvert != -1) { mbres = ASN1_mbstring_ncopy(&a1utf, data, len, mbconvert, B_ASN1_UTF8STRING, minchars, maxchars); if (mbres < 0) { tls_set_error_libssl(ctx, "invalid %s", desc); goto failed; } if (mbres != V_ASN1_UTF8STRING) { tls_set_errorx(ctx, "multibyte conversion failed: expected UTF8 result"); goto failed; } data = ASN1_STRING_data(a1utf); len = ASN1_STRING_length(a1utf); } /* must not allow \0 */ if (memchr(data, 0, len) != NULL) { tls_set_errorx(ctx, "invalid %s: contains NUL", desc); goto failed; } /* no escape codes please */ if (check_invalid_bytes(ctx, data, len, ascii_only, desc) < 0) goto failed; /* copy to new string */ cstr = malloc(len + 1); if (!cstr) { tls_set_error(ctx, "malloc"); goto failed; } memcpy(cstr, data, len); cstr[len] = 0; *dst_p = cstr; ret = len; failed: ASN1_STRING_free(a1utf); return ret; } static int tls_cert_get_dname_string(struct tls *ctx, X509_NAME *name, int nid, const char **str_p, int minchars, int maxchars, const char *desc) { int loc, len; X509_NAME_ENTRY *ne; ASN1_STRING *a1str; *str_p = NULL; loc = X509_NAME_get_index_by_NID(name, nid, -1); if (loc < 0) return 0; ne = X509_NAME_get_entry(name, loc); if (!ne) return 0; a1str = X509_NAME_ENTRY_get_data(ne); if (!a1str) return 0; len = tls_parse_asn1string(ctx, a1str, str_p, minchars, maxchars, desc); if (len < 0) return -1; return 0; } static int tls_load_alt_ia5string(struct tls *ctx, ASN1_IA5STRING *ia5str, struct tls_cert *cert, int slot_type, int minchars, int maxchars, const char *desc) { struct tls_cert_general_name *slot; const char *data; int len; slot = &cert->subject_alt_names[cert->subject_alt_name_count]; len = tls_parse_asn1string(ctx, ia5str, &data, minchars, maxchars, desc); if (len < 0) return 0; /* * Per RFC 5280 section 4.2.1.6: * " " is a legal domain name, but that * dNSName must be rejected. */ if (len == 1 && data[0] == ' ') { tls_set_errorx(ctx, "invalid %s: single space", desc); return -1; } slot->name_value = data; slot->name_type = slot_type; cert->subject_alt_name_count++; return 0; } static int tls_load_alt_ipaddr(struct tls *ctx, ASN1_OCTET_STRING *bin, struct tls_cert *cert) { struct tls_cert_general_name *slot; void *data; int len; slot = &cert->subject_alt_names[cert->subject_alt_name_count]; len = ASN1_STRING_length(bin); data = ASN1_STRING_data(bin); if (len < 0) { tls_set_errorx(ctx, "negative length for ipaddress"); return -1; } /* * Per RFC 5280 section 4.2.1.6: * IPv4 must use 4 octets and IPv6 must use 16 octets. */ if (len == 4) { slot->name_type = TLS_CERT_GNAME_IPv4; } else if (len == 16) { slot->name_type = TLS_CERT_GNAME_IPv6; } else { tls_set_errorx(ctx, "invalid length for ipaddress"); return -1; } slot->name_value = malloc(len); if (slot->name_value == NULL) { tls_set_error(ctx, "malloc"); return -1; } memcpy((void *)slot->name_value, data, len); cert->subject_alt_name_count++; return 0; } /* See RFC 5280 section 4.2.1.6 for SubjectAltName details. */ static int tls_cert_get_altnames(struct tls *ctx, struct tls_cert *cert, X509 *x509_cert) { STACK_OF(GENERAL_NAME) *altname_stack = NULL; GENERAL_NAME *altname; int count, i; int rv = -1; altname_stack = X509_get_ext_d2i(x509_cert, NID_subject_alt_name, NULL, NULL); if (altname_stack == NULL) return 0; count = sk_GENERAL_NAME_num(altname_stack); if (count == 0) { rv = 0; goto out; } cert->subject_alt_names = calloc(sizeof (struct tls_cert_general_name), count); if (cert->subject_alt_names == NULL) { tls_set_error(ctx, "calloc"); goto out; } for (i = 0; i < count; i++) { altname = sk_GENERAL_NAME_value(altname_stack, i); if (altname->type == GEN_DNS) { rv = tls_load_alt_ia5string(ctx, altname->d.dNSName, cert, TLS_CERT_GNAME_DNS, 1, UB_GNAME_DNS, "dns"); } else if (altname->type == GEN_EMAIL) { rv = tls_load_alt_ia5string(ctx, altname->d.rfc822Name, cert, TLS_CERT_GNAME_EMAIL, 1, UB_GNAME_EMAIL, "email"); } else if (altname->type == GEN_URI) { rv = tls_load_alt_ia5string(ctx, altname->d.uniformResourceIdentifier, cert, TLS_CERT_GNAME_URI, 1, UB_GNAME_URI, "uri"); } else if (altname->type == GEN_IPADD) { rv = tls_load_alt_ipaddr(ctx, altname->d.iPAddress, cert); } else { /* ignore unknown types */ rv = 0; } if (rv < 0) goto out; } rv = 0; out: sk_GENERAL_NAME_pop_free(altname_stack, GENERAL_NAME_free); return rv; } static int tls_get_dname(struct tls *ctx, X509_NAME *name, struct tls_cert_dname *dname) { int ret; ret = tls_cert_get_dname_string(ctx, name, NID_commonName, &dname->common_name, 0, UB_COMMON_NAME, "commonName"); if (ret == 0) ret = tls_cert_get_dname_string(ctx, name, NID_countryName, &dname->country_name, 0, UB_COUNTRY_NAME, "countryName"); if (ret == 0) ret = tls_cert_get_dname_string(ctx, name, NID_stateOrProvinceName, &dname->state_or_province_name, 0, UB_STATE_NAME, "stateName"); if (ret == 0) ret = tls_cert_get_dname_string(ctx, name, NID_localityName, &dname->locality_name, 0, UB_LOCALITY_NAME, "localityName"); if (ret == 0) ret = tls_cert_get_dname_string(ctx, name, NID_streetAddress, &dname->street_address, 0, UB_STREET_ADDRESS, "streetAddress"); if (ret == 0) ret = tls_cert_get_dname_string(ctx, name, NID_organizationName, &dname->organization_name, 0, UB_ORGANIZATION_NAME, "organizationName"); if (ret == 0) ret = tls_cert_get_dname_string(ctx, name, NID_organizationalUnitName, &dname->organizational_unit_name, 0, UB_ORGANIZATIONAL_UNIT_NAME, "organizationalUnitName"); return ret; } static int tls_get_basic_constraints(struct tls *ctx, struct tls_cert *cert, X509 *x509) { BASIC_CONSTRAINTS *bc; int crit; int ret = -1; bc = X509_get_ext_d2i(x509, NID_basic_constraints, &crit, NULL); if (!bc) return 0; cert->ext_set |= TLS_EXT_BASIC; if (crit) cert->ext_crit |= TLS_EXT_BASIC; cert->basic_constraints_ca = bc->ca ? 1 : 0; if (bc->pathlen) { cert->basic_constraints_pathlen = ASN1_INTEGER_get(bc->pathlen); if (cert->basic_constraints_pathlen < 0) { tls_set_error(ctx, "BasicConstraints has invalid pathlen"); goto failed; } } else { cert->basic_constraints_pathlen = -1; } ret = 0; failed: BASIC_CONSTRAINTS_free(bc); return ret; } static uint32_t map_bits(const uint32_t map[][2], uint32_t input) { uint32_t i, out = 0; for (i = 0; map[i][0]; i++) { if (map[i][0] & input) out |= map[i][1]; } return out; } static int tls_get_key_usage(struct tls *ctx, struct tls_cert *cert, X509 *x509) { static const uint32_t ku_map[][2] = { {KU_DIGITAL_SIGNATURE, KU_DIGITAL_SIGNATURE}, {KU_NON_REPUDIATION, KU_NON_REPUDIATION}, {KU_KEY_ENCIPHERMENT, KU_KEY_ENCIPHERMENT}, {KU_DATA_ENCIPHERMENT, KU_DATA_ENCIPHERMENT}, {KU_KEY_AGREEMENT, KU_KEY_AGREEMENT}, {KU_KEY_CERT_SIGN, KU_KEY_CERT_SIGN}, {KU_CRL_SIGN, KU_CRL_SIGN}, {KU_ENCIPHER_ONLY, KU_ENCIPHER_ONLY}, {KU_DECIPHER_ONLY, KU_DECIPHER_ONLY}, {0, 0}, }; ASN1_BIT_STRING *ku; int crit; ku = X509_get_ext_d2i(x509, NID_key_usage, &crit, NULL); if (!ku) return 0; cert->ext_set |= TLS_EXT_KEY_USAGE; if (crit) cert->ext_crit |= TLS_EXT_KEY_USAGE; ASN1_BIT_STRING_free(ku); cert->key_usage_flags = map_bits(ku_map, x509->ex_kusage); return 0; } static int tls_get_ext_key_usage(struct tls *ctx, struct tls_cert *cert, X509 *x509) { static const uint32_t xku_map[][2] = { {XKU_SSL_SERVER, TLS_XKU_SSL_SERVER}, {XKU_SSL_CLIENT, TLS_XKU_SSL_CLIENT}, {XKU_SMIME, TLS_XKU_SMIME}, {XKU_CODE_SIGN, TLS_XKU_CODE_SIGN}, {XKU_SGC, TLS_XKU_SGC}, {XKU_OCSP_SIGN, TLS_XKU_OCSP_SIGN}, {XKU_TIMESTAMP, TLS_XKU_TIMESTAMP}, {XKU_DVCS, TLS_XKU_DVCS}, {0, 0}, }; EXTENDED_KEY_USAGE *xku; int crit; xku = X509_get_ext_d2i(x509, NID_ext_key_usage, &crit, NULL); if (!xku) return 0; sk_ASN1_OBJECT_pop_free(xku, ASN1_OBJECT_free); cert->ext_set |= TLS_EXT_EXTENDED_KEY_USAGE; if (crit) cert->ext_crit |= TLS_EXT_EXTENDED_KEY_USAGE; cert->extended_key_usage_flags = map_bits(xku_map, x509->ex_xkusage); return 0; } static int tls_load_extensions(struct tls *ctx, struct tls_cert *cert, X509 *x509) { int ret; /* * Force libssl to fill extension fields under X509 struct. * Then libtls does not need to parse raw data. */ X509_check_ca(x509); ret = tls_get_basic_constraints(ctx, cert, x509); if (ret == 0) ret = tls_get_key_usage(ctx, cert, x509); if (ret == 0) ret = tls_get_ext_key_usage(ctx, cert, x509); if (ret == 0) ret = tls_cert_get_altnames(ctx, cert, x509); return ret; } static void * tls_calc_fingerprint(struct tls *ctx, X509 *x509, const char *algo, size_t *outlen) { const EVP_MD *md; void *res; int ret; unsigned int tmplen, mdlen; if (outlen) *outlen = 0; if (strcasecmp(algo, "sha1") == 0) { md = EVP_sha1(); } else if (strcasecmp(algo, "sha256") == 0) { md = EVP_sha256(); } else { tls_set_errorx(ctx, "invalid fingerprint algorithm"); return NULL; } mdlen = EVP_MD_size(md); res = malloc(mdlen); if (!res) { tls_set_error(ctx, "malloc"); return NULL; } ret = X509_digest(x509, md, res, &tmplen); if (ret != 1 || tmplen != mdlen) { free(res); tls_set_errorx(ctx, "X509_digest failed"); return NULL; } if (outlen) *outlen = mdlen; return res; } static void check_verify_error(struct tls *ctx, struct tls_cert *cert) { long vres = SSL_get_verify_result(ctx->ssl_conn); if (vres == X509_V_OK) { cert->successful_verify = 1; } else { cert->successful_verify = 0; } } int tls_parse_cert(struct tls *ctx, struct tls_cert **cert_p, const char *fingerprint_algo, X509 *x509) { struct tls_cert *cert = NULL; X509_NAME *subject, *issuer; int ret = -1; long version; *cert_p = NULL; version = X509_get_version(x509); if (version < 0) { tls_set_errorx(ctx, "invalid version"); return -1; } subject = X509_get_subject_name(x509); if (!subject) { tls_set_errorx(ctx, "cert does not have subject"); return -1; } issuer = X509_get_issuer_name(x509); if (!issuer) { tls_set_errorx(ctx, "cert does not have issuer"); return -1; } cert = calloc(sizeof *cert, 1); if (!cert) { tls_set_error(ctx, "calloc"); goto failed; } cert->version = version; if (fingerprint_algo) { cert->fingerprint = tls_calc_fingerprint(ctx, x509, fingerprint_algo, &cert->fingerprint_size); if (!cert->fingerprint) goto failed; } ret = tls_get_dname(ctx, subject, &cert->subject); if (ret == 0) ret = tls_get_dname(ctx, issuer, &cert->issuer); if (ret == 0) ret = tls_asn1_parse_time(ctx, X509_get_notBefore(x509), &cert->not_before); if (ret == 0) ret = tls_asn1_parse_time(ctx, X509_get_notAfter(x509), &cert->not_after); if (ret == 0) ret = tls_parse_bigint(ctx, X509_get_serialNumber(x509), &cert->serial); if (ret == 0) ret = tls_load_extensions(ctx, cert, x509); if (ret == 0) { *cert_p = cert; return 0; } failed: tls_cert_free(cert); return ret; } int tls_get_peer_cert(struct tls *ctx, struct tls_cert **cert_p, const char *fingerprint_algo) { X509 *peer = ctx->ssl_peer_cert; int res; *cert_p = NULL; if (!peer) { tls_set_errorx(ctx, "peer does not have cert"); return TLS_NO_CERT; } ERR_clear_error(); res = tls_parse_cert(ctx, cert_p, fingerprint_algo, peer); if (res == 0) check_verify_error(ctx, *cert_p); ERR_clear_error(); return res; } static void tls_cert_free_dname(struct tls_cert_dname *dname) { free((void*)dname->common_name); free((void*)dname->country_name); free((void*)dname->state_or_province_name); free((void*)dname->locality_name); free((void*)dname->street_address); free((void*)dname->organization_name); free((void*)dname->organizational_unit_name); } void tls_cert_free(struct tls_cert *cert) { int i; if (!cert) return; tls_cert_free_dname(&cert->issuer); tls_cert_free_dname(&cert->subject); if (cert->subject_alt_name_count) { for (i = 0; i < cert->subject_alt_name_count; i++) free((void*)cert->subject_alt_names[i].name_value); } free(cert->subject_alt_names); free((void*)cert->serial); free((void*)cert->fingerprint); free(cert); } #endif /* USUAL_LIBSSL_FOR_TLS */ pgbouncer-1.7/lib/usual/tls/tls_server.c0000664000175000017500000001115612615704017015334 00000000000000/* $OpenBSD: tls_server.c,v 1.6 2015/03/31 12:21:27 jsing Exp $ */ /* * Copyright (c) 2014 Joel Sing * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "tls_compat.h" #ifdef USUAL_LIBSSL_FOR_TLS #include #include #include #include #include "tls_internal.h" struct tls * tls_server(void) { struct tls *ctx; if ((ctx = tls_new()) == NULL) return (NULL); ctx->flags |= TLS_SERVER; return (ctx); } struct tls * tls_server_conn(struct tls *ctx) { struct tls *conn_ctx; if ((conn_ctx = tls_new()) == NULL) return (NULL); conn_ctx->flags |= TLS_SERVER_CONN; return (conn_ctx); } int tls_configure_server(struct tls *ctx) { EC_KEY *ecdh_key; unsigned char sid[SSL_MAX_SSL_SESSION_ID_LENGTH]; if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) { tls_set_errorx(ctx, "ssl context failure"); goto err; } if (tls_configure_ssl(ctx) != 0) goto err; if (tls_configure_keypair(ctx, 1) != 0) goto err; if (ctx->config->verify_client != 0) { int verify = SSL_VERIFY_PEER; if (ctx->config->verify_client == 1) verify |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; if (tls_configure_ssl_verify(ctx, verify) == -1) goto err; } if (ctx->config->dheparams == -1) SSL_CTX_set_dh_auto(ctx->ssl_ctx, 1); else if (ctx->config->dheparams == 1024) SSL_CTX_set_dh_auto(ctx->ssl_ctx, 2); if (ctx->config->ecdhecurve == -1) { SSL_CTX_set_ecdh_auto(ctx->ssl_ctx, 1); } else if (ctx->config->ecdhecurve != NID_undef) { if ((ecdh_key = EC_KEY_new_by_curve_name( ctx->config->ecdhecurve)) == NULL) { tls_set_errorx(ctx, "failed to set ECDHE curve"); goto err; } SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_SINGLE_ECDH_USE); SSL_CTX_set_tmp_ecdh(ctx->ssl_ctx, ecdh_key); EC_KEY_free(ecdh_key); } if (ctx->config->ciphers_server == 1) SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); if (SSL_CTX_set_tlsext_status_cb(ctx->ssl_ctx, tls_ocsp_stapling_callback) != 1) { tls_set_errorx(ctx, "ssl OCSP stapling setup failure"); goto err; } /* * Set session ID context to a random value. We don't support * persistent caching of sessions so it is OK to set a temporary * session ID context that is valid during run time. */ if (!RAND_bytes(sid, sizeof(sid))) { tls_set_errorx(ctx, "failed to generate session id"); goto err; } if (!SSL_CTX_set_session_id_context(ctx->ssl_ctx, sid, sizeof(sid))) { tls_set_errorx(ctx, "failed to set session id context"); goto err; } return (0); err: return (-1); } int tls_accept_socket(struct tls *ctx, struct tls **cctx, int socket) { return (tls_accept_fds(ctx, cctx, socket, socket)); } int tls_accept_fds(struct tls *ctx, struct tls **cctx, int fd_read, int fd_write) { struct tls *conn_ctx = NULL; if ((ctx->flags & TLS_SERVER) == 0) { tls_set_errorx(ctx, "not a server context"); goto err; } if ((conn_ctx = tls_server_conn(ctx)) == NULL) { tls_set_errorx(ctx, "connection context failure"); goto err; } if ((conn_ctx->ssl_conn = SSL_new(ctx->ssl_ctx)) == NULL) { tls_set_errorx(ctx, "ssl failure"); goto err; } if (SSL_set_app_data(conn_ctx->ssl_conn, conn_ctx) != 1) { tls_set_errorx(ctx, "ssl application data failure"); goto err; } if (SSL_set_rfd(conn_ctx->ssl_conn, fd_read) != 1 || SSL_set_wfd(conn_ctx->ssl_conn, fd_write) != 1) { tls_set_errorx(ctx, "ssl file descriptor failure"); goto err; } *cctx = conn_ctx; return (0); err: tls_free(conn_ctx); *cctx = NULL; return (-1); } int tls_handshake_server(struct tls *ctx) { int ssl_ret; int rv = -1; if ((ctx->flags & TLS_SERVER_CONN) == 0) { tls_set_errorx(ctx, "not a server connection context"); goto err; } ERR_clear_error(); if ((ssl_ret = SSL_accept(ctx->ssl_conn)) != 1) { rv = tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "handshake"); goto err; } ctx->state |= TLS_HANDSHAKE_COMPLETE; rv = 0; err: return (rv); } #endif /* USUAL_LIBSSL_FOR_TLS */ pgbouncer-1.7/lib/usual/tls/tls_compat.h0000664000175000017500000000207012576101475015316 00000000000000 #ifndef _USUAL_TLS_COMPAT_H_ #define _USUAL_TLS_COMPAT_H_ #include #ifdef USUAL_LIBSSL_FOR_TLS #include #include #include #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) #define USE_LIBSSL_INTERNALS #endif /* ecdh_auto is broken - ignores main EC key */ #undef SSL_CTX_set_ecdh_auto /* dh_auto seems fine, but use ours to get DH info */ #undef SSL_CTX_set_dh_auto #ifndef SSL_CTX_set_dh_auto long SSL_CTX_set_dh_auto(SSL_CTX *ctx, int onoff); #endif #ifndef SSL_CTX_set_ecdh_auto long SSL_CTX_set_ecdh_auto(SSL_CTX *ctx, int onoff); #endif #ifndef HAVE_SSL_CTX_USE_CERTIFICATE_CHAIN_MEM int SSL_CTX_use_certificate_chain_mem(SSL_CTX *ctx, void *buf, int len); #endif #ifndef HAVE_SSL_CTX_LOAD_VERIFY_MEM int SSL_CTX_load_verify_mem(SSL_CTX *ctx, void *buf, int len); #endif #ifdef OPENSSL_IS_BORINGSSL #define SSL_CTX_set_tlsext_status_cb(a,b) (1) #define SSL_set_tlsext_status_type(a,b) (1) #endif #endif /* USUAL_LIBSSL_FOR_TLS */ #endif /* _USUAL_TLS_COMPAT_H_ */ pgbouncer-1.7/lib/usual/tls/tls_conninfo.c0000664000175000017500000001172612615704017015642 00000000000000/* $OpenBSD: tls_peer.c,v 1.3 2015/09/11 13:22:39 beck Exp $ */ /* * Copyright (c) 2015 Joel Sing * Copyright (c) 2015 Bob Beck * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "tls_compat.h" #ifdef USUAL_LIBSSL_FOR_TLS #include #include #include "tls_internal.h" static int tls_hex_string(const unsigned char *in, size_t inlen, char **out, size_t *outlen) { static const char hex[] = "0123456789abcdef"; size_t i, len; char *p; if (outlen != NULL) *outlen = 0; if (inlen >= SIZE_MAX) return (-1); if ((*out = reallocarray(NULL, inlen + 1, 2)) == NULL) return (-1); p = *out; len = 0; for (i = 0; i < inlen; i++) { p[len++] = hex[(in[i] >> 4) & 0x0f]; p[len++] = hex[in[i] & 0x0f]; } p[len++] = 0; if (outlen != NULL) *outlen = len; return (0); } static int tls_get_peer_cert_hash(struct tls *ctx, char **hash) { unsigned char d[EVP_MAX_MD_SIZE]; char *dhex = NULL; unsigned int dlen; int rv = -1; *hash = NULL; if (ctx->ssl_peer_cert == NULL) return (0); if (X509_digest(ctx->ssl_peer_cert, EVP_sha256(), d, &dlen) != 1) { tls_set_errorx(ctx, "digest failed"); goto err; } if (tls_hex_string(d, dlen, &dhex, NULL) != 0) { tls_set_errorx(ctx, "digest hex string failed"); goto err; } if (asprintf(hash, "SHA256:%s", dhex) == -1) { tls_set_errorx(ctx, "out of memory"); *hash = NULL; goto err; } rv = 0; err: free(dhex); return (rv); } static int tls_get_peer_cert_issuer(struct tls *ctx, char **issuer) { X509_NAME *name = NULL; *issuer = NULL; if (ctx->ssl_peer_cert == NULL) return (-1); if ((name = X509_get_issuer_name(ctx->ssl_peer_cert)) == NULL) return (-1); *issuer = X509_NAME_oneline(name, 0, 0); if (*issuer == NULL) return (-1); return (0); } static int tls_get_peer_cert_subject(struct tls *ctx, char **subject) { X509_NAME *name = NULL; *subject = NULL; if (ctx->ssl_peer_cert == NULL) return (-1); if ((name = X509_get_subject_name(ctx->ssl_peer_cert)) == NULL) return (-1); *subject = X509_NAME_oneline(name, 0, 0); if (*subject == NULL) return (-1); return (0); } static int tls_get_peer_cert_times(struct tls *ctx, time_t *notbefore, time_t *notafter) { struct tm before_tm, after_tm; ASN1_TIME *before, *after; int rv = -1; memset(&before_tm, 0, sizeof(before_tm)); memset(&after_tm, 0, sizeof(after_tm)); if (ctx->ssl_peer_cert != NULL) { if ((before = X509_get_notBefore(ctx->ssl_peer_cert)) == NULL) goto err; if ((after = X509_get_notAfter(ctx->ssl_peer_cert)) == NULL) goto err; if (asn1_time_parse((char*)before->data, before->length, &before_tm, 0) == -1) goto err; if (asn1_time_parse((char*)after->data, after->length, &after_tm, 0) == -1) goto err; if ((*notbefore = timegm(&before_tm)) == -1) goto err; if ((*notafter = timegm(&after_tm)) == -1) goto err; } rv = 0; err: return (rv); } int tls_get_conninfo(struct tls *ctx) { const char * tmp; if (ctx->ssl_peer_cert != NULL) { if (tls_get_peer_cert_hash(ctx, &ctx->conninfo->hash) == -1) goto err; if (tls_get_peer_cert_subject(ctx, &ctx->conninfo->subject) == -1) goto err; if (tls_get_peer_cert_issuer(ctx, &ctx->conninfo->issuer) == -1) goto err; if (tls_get_peer_cert_times(ctx, &ctx->conninfo->notbefore, &ctx->conninfo->notafter) == -1) goto err; } if ((tmp = SSL_get_version(ctx->ssl_conn)) == NULL) goto err; ctx->conninfo->version = strdup(tmp); if (ctx->conninfo->version == NULL) goto err; if ((tmp = SSL_get_cipher(ctx->ssl_conn)) == NULL) goto err; ctx->conninfo->cipher = strdup(tmp); if (ctx->conninfo->cipher == NULL) goto err; return (0); err: tls_free_conninfo(ctx->conninfo); return (-1); } void tls_free_conninfo(struct tls_conninfo *conninfo) { if (conninfo != NULL) { free(conninfo->hash); conninfo->hash = NULL; free(conninfo->subject); conninfo->subject = NULL; free(conninfo->issuer); conninfo->issuer = NULL; free(conninfo->version); conninfo->version = NULL; free(conninfo->cipher); conninfo->cipher = NULL; } } const char * tls_conn_cipher(struct tls *ctx) { if (ctx->conninfo == NULL) return (NULL); return (ctx->conninfo->cipher); } const char * tls_conn_version(struct tls *ctx) { if (ctx->conninfo == NULL) return (NULL); return (ctx->conninfo->version); } #endif pgbouncer-1.7/lib/usual/tls/tls_ocsp.c0000664000175000017500000005302012615704017014766 00000000000000/* * Copyright (c) 2015 Marko Kreen * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "tls_compat.h" #ifdef USUAL_LIBSSL_FOR_TLS #include #include "tls_internal.h" #ifndef OPENSSL_NO_OCSP #include #define MAXAGE_SEC (14*24*60*60) #define JITTER_SEC (60) #define USE_NONCE 0 #if defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER < 0x10002000 #define BUGGY_VERIFY #endif /* * State for request. */ struct tls_ocsp_query { /* responder location */ char *ocsp_url; /* request blob */ uint8_t *request_data; size_t request_size; /* network state */ BIO *bio; SSL_CTX *ssl_ctx; OCSP_REQ_CTX *http_req; /* cert data, this struct does not own these */ X509 *main_cert; STACK_OF(X509) *extra_certs; SSL_CTX *cert_ssl_ctx; }; /* * Extract OCSP response info. */ static int tls_ocsp_fill_info(struct tls *ctx, int response_status, int cert_status, int crl_reason, ASN1_GENERALIZEDTIME *revtime, ASN1_GENERALIZEDTIME *thisupd, ASN1_GENERALIZEDTIME *nextupd) { struct tls_ocsp_info *info; int res; info = calloc(1, sizeof (struct tls_ocsp_info)); if (!info) { tls_set_error(ctx, "calloc"); return -1; } info->response_status = response_status; info->cert_status = cert_status; info->crl_reason = crl_reason; res = tls_asn1_parse_time(ctx, revtime, &info->revocation_time); if (res == 0) res = tls_asn1_parse_time(ctx, thisupd, &info->this_update); if (res == 0) res = tls_asn1_parse_time(ctx, nextupd, &info->next_update); if (res == 0) { ctx->ocsp_info = info; } else { tls_ocsp_info_free(info); } return res; } static void tls_ocsp_fill_result(struct tls *ctx, int res) { struct tls_ocsp_info *info = ctx->ocsp_info; if (res < 0) { ctx->ocsp_result = "error"; } else if (info->response_status != OCSP_RESPONSE_STATUS_SUCCESSFUL) { ctx->ocsp_result = OCSP_response_status_str(info->response_status); } else if (info->cert_status != V_OCSP_CERTSTATUS_REVOKED) { ctx->ocsp_result = OCSP_cert_status_str(info->cert_status); } else { ctx->ocsp_result = OCSP_crl_reason_str(info->crl_reason); } } void tls_ocsp_info_free(struct tls_ocsp_info *info) { free(info); } int tls_get_ocsp_info(struct tls *ctx, int *response_status, int *cert_status, int *crl_reason, time_t *this_update, time_t *next_update, time_t *revoction_time, const char **result_text) { static const struct tls_ocsp_info no_ocsp = { -1, -1, -1, 0, 0, 0 }; const struct tls_ocsp_info *info = ctx->ocsp_info; const char *ocsp_result = ctx->ocsp_result; int ret = 0; if (!info) { info = &no_ocsp; ret = -1; } if (!ocsp_result) { ret = TLS_NO_OCSP; ocsp_result = "no-ocsp"; } if (response_status) *response_status = info->response_status; if (cert_status) *cert_status = info->cert_status; if (crl_reason) *crl_reason = info->crl_reason; if (this_update) *this_update = info->this_update; if (next_update) *next_update = info->next_update; if (revoction_time) *revoction_time = info->revocation_time; if (result_text) *result_text = ocsp_result; return ret; } /* * Verify stapled response */ static OCSP_CERTID * tls_ocsp_get_certid(X509 *main_cert, STACK_OF(X509) *extra_certs, SSL_CTX *ssl_ctx) { X509_NAME *issuer_name; X509 *issuer; X509_STORE_CTX storectx; X509_OBJECT tmpobj; OCSP_CERTID *cid = NULL; X509_STORE *store; int ok; issuer_name = X509_get_issuer_name(main_cert); if (!issuer_name) return NULL; if (extra_certs) { issuer = X509_find_by_subject(extra_certs, issuer_name); if (issuer) return OCSP_cert_to_id(NULL, main_cert, issuer); } store = SSL_CTX_get_cert_store(ssl_ctx); if (!store) return NULL; ok = X509_STORE_CTX_init(&storectx, store, main_cert, extra_certs); if (ok != 1) return NULL; ok = X509_STORE_get_by_subject(&storectx, X509_LU_X509, issuer_name, &tmpobj); if (ok == 1) { cid = OCSP_cert_to_id(NULL, main_cert, tmpobj.data.x509); X509_free(tmpobj.data.x509); } X509_STORE_CTX_cleanup(&storectx); return cid; } static int tls_ocsp_verify_response(struct tls *ctx, X509 *main_cert, STACK_OF(X509) *extra_certs, SSL_CTX *ssl_ctx, OCSP_RESPONSE *resp) { OCSP_BASICRESP *br = NULL; STACK_OF(X509) *ocsp_chain = NULL; ASN1_GENERALIZEDTIME *revtime = NULL, *thisupd = NULL, *nextupd = NULL; OCSP_CERTID *cid = NULL; STACK_OF(X509) *combined = NULL; int response_status=0, cert_status=0, crl_reason=0; int ssl_res, ret = -1; unsigned long flags; #ifdef BUGGY_VERIFY STACK_OF(X509) *tmpchain = NULL; #endif br = OCSP_response_get1_basic(resp); if (!br) { tls_set_errorx(ctx, "ocsp error: cannot load"); goto error; } #ifdef BUGGY_VERIFY /* * There may be OCSP-subCA in OCSP response that chains to subCA * in main TLS headers. Need to present both chains to verify. * * OCSP_basic_verify() bugs: * - Does not use br->certs when building chain. * - Does not use main_certs when validating br->certs. */ if (br->certs && extra_certs) { int i; combined = sk_X509_new_null(); if (!combined) { tls_set_errorx(ctx, "ocsp error: cannot merge"); goto error; } for (i = 0; i < sk_X509_num(br->certs); i++) { ssl_res = sk_X509_push(combined, sk_X509_value(br->certs, i)); if (ssl_res == 0) { tls_set_errorx(ctx, "ocsp error: cannot merge"); goto error; } } for (i = 0; i < sk_X509_num(extra_certs); i++) { ssl_res = sk_X509_push(combined, sk_X509_value(extra_certs, i)); if (ssl_res == 0) { tls_set_errorx(ctx, "ocsp error: cannot merge"); goto error; } } /* OCSP_TRUSTOTHER would skip br->certs checks */ flags = 0; flags = OCSP_TRUSTOTHER; ocsp_chain = combined; /* Bug: does not use main_certs when validating br->certs */ if ((flags & OCSP_TRUSTOTHER) == 0) { tmpchain = br->certs; br->certs = combined; } } else if (br->certs && !extra_certs) { /* can this happen? */ flags = 0; ocsp_chain = br->certs; } else #endif { /* * Skip validation of 'extra_certs' as this should be done * already as part of main handshake. */ flags = OCSP_TRUSTOTHER; ocsp_chain = extra_certs; } /* now verify */ ssl_res = OCSP_basic_verify(br, ocsp_chain, SSL_CTX_get_cert_store(ssl_ctx), flags); #ifdef BUGGY_VERIFY /* replace back */ if (tmpchain) { br->certs = tmpchain; tmpchain = NULL; } #endif if (ssl_res != 1) { tls_set_error_libssl(ctx, "ocsp verify failed"); goto error; } /* signature OK, look inside */ response_status = OCSP_response_status(resp); if (response_status != OCSP_RESPONSE_STATUS_SUCCESSFUL) { tls_set_errorx(ctx, "ocsp verify failed: unsuccessful response - %s", OCSP_response_status_str(response_status)); goto error; } cid = tls_ocsp_get_certid(main_cert, extra_certs, ssl_ctx); if (!cid) { tls_set_errorx(ctx, "ocsp verify failed: no issuer cert"); goto error; } ssl_res = OCSP_resp_find_status(br, cid, &cert_status, &crl_reason, &revtime, &thisupd, &nextupd); if (ssl_res != 1) { tls_set_errorx(ctx, "ocsp verify failed: no result for cert"); goto error; } ssl_res = OCSP_check_validity(thisupd, nextupd, JITTER_SEC, MAXAGE_SEC); if (ssl_res != 1) { tls_set_errorx(ctx, "ocsp verify failed: bad age"); goto error; } ssl_res = tls_ocsp_fill_info(ctx, response_status, cert_status, crl_reason, revtime, thisupd, nextupd); if (ssl_res != 0) goto error; /* finally can look at status */ if (cert_status != V_OCSP_CERTSTATUS_GOOD && cert_status != V_OCSP_CERTSTATUS_UNKNOWN) { tls_set_errorx(ctx, "ocsp verify failed: revoked cert - %s", OCSP_crl_reason_str(crl_reason)); goto error; } ret = 0; error: sk_X509_free(combined); OCSP_CERTID_free(cid); OCSP_BASICRESP_free(br); return ret; } /* * Same callback on client-side has different error proto: * 1=OK, 0=bad, -1=internal error */ int tls_ocsp_verify_callback(SSL *ssl, void *arg) { OCSP_RESPONSE *resp = NULL; STACK_OF(X509) *extra_certs = NULL; X509 *peer = NULL; const unsigned char *raw = NULL; int size, res = -1; struct tls *ctx; ctx = SSL_get_app_data(ssl); if (!ctx) return -1; size = SSL_get_tlsext_status_ocsp_resp(ssl, &raw); if (size <= 0) return 1; peer = SSL_get_peer_certificate(ssl); if (!peer) { tls_set_errorx(ctx, "ocsp verify failed: no peer cert"); goto error; } resp = d2i_OCSP_RESPONSE(NULL, &raw, size); if (!resp) { tls_set_errorx(ctx, "ocsp verify failed: parse failed"); goto error; } extra_certs = SSL_get_peer_cert_chain(ssl); res = tls_ocsp_verify_response(ctx, peer, extra_certs, ctx->ssl_ctx, resp); error: tls_ocsp_fill_result(ctx, res); OCSP_RESPONSE_free(resp); X509_free(peer); return (res == 0) ? 1 : 0; } /* * Staple OCSP response to server handshake. */ int tls_ocsp_stapling_callback(SSL *ssl, void *arg) { struct tls *ctx; char *mem, *fmem = NULL; uint8_t *xmem; size_t len; int ret = SSL_TLSEXT_ERR_ALERT_FATAL; ctx = SSL_get_app_data(ssl); if (!ctx) return SSL_TLSEXT_ERR_NOACK; if (ctx->config->ocsp_file) { fmem = mem = (char*)tls_load_file(ctx->config->ocsp_file, &len, NULL); if (!mem) goto err; } else { mem = ctx->config->ocsp_mem; len = ctx->config->ocsp_len; if (!mem) return SSL_TLSEXT_ERR_NOACK; } xmem = OPENSSL_malloc(len); if (xmem) { memcpy(xmem, mem, len); if (SSL_set_tlsext_status_ocsp_resp(ctx->ssl_conn, xmem, len) != 1) { OPENSSL_free(xmem); goto err; } ret = SSL_TLSEXT_ERR_OK; } err: free(fmem); return ret; } /* * Query OCSP responder over HTTP(S). */ void tls_ocsp_client_free(struct tls *ctx) { struct tls_ocsp_query *q; if (!ctx) return; q = ctx->ocsp_query; if (q) { if (q->http_req) OCSP_REQ_CTX_free(q->http_req); BIO_free_all(q->bio); SSL_CTX_free(q->ssl_ctx); free(q->ocsp_url); free(q->request_data); free(q); ctx->ocsp_query = NULL; } } static struct tls * tls_ocsp_client_new(void) { struct tls *ctx; ctx = tls_new(); if (!ctx) return NULL; ctx->flags = TLS_OCSP_CLIENT; ctx->ocsp_query = calloc(1, sizeof (struct tls_ocsp_query)); if (!ctx->ocsp_query) { tls_free(ctx); return NULL; } return ctx; } static int tls_build_ocsp_request(struct tls *ctx) { struct tls_ocsp_query *q; int ok, ret = -1; OCSP_REQUEST *req = NULL; OCSP_CERTID *cid = NULL; OCSP_ONEREQ *onereq = NULL; BIO *mem = NULL; void *data; q = ctx->ocsp_query; cid = tls_ocsp_get_certid(q->main_cert, q->extra_certs, q->cert_ssl_ctx); if (!cid) { tls_set_errorx(ctx, "Cannot create cert-id"); goto failed; } req = OCSP_REQUEST_new(); if (!req) { tls_set_error_libssl(ctx, "Cannot create request"); goto failed; } onereq = OCSP_request_add0_id(req, cid); if (!onereq) { tls_set_error_libssl(ctx, "Cannot add cert-id to request"); goto failed; } cid = NULL; /* * Now render it. */ mem = BIO_new(BIO_s_mem()); if (!mem) { tls_set_errorx(ctx, "BIO_new"); goto failed; } ok = i2d_OCSP_REQUEST_bio(mem, req); if (!ok) { tls_set_error_libssl(ctx, "i2d_OCSP_RESPONSE_bio"); goto failed; } q->request_size = BIO_get_mem_data(mem, &data); q->request_data = malloc(q->request_size); if (!q->request_data) { tls_set_error(ctx, "Failed to allocate request data"); goto failed; } memcpy(q->request_data, data, q->request_size); req = NULL; ret = 0; failed: OCSP_CERTID_free(cid); OCSP_REQUEST_free(req); BIO_free(mem); return ret; } static int tls_ocsp_setup(struct tls **ocsp_ctx_p, struct tls_config *config, struct tls *target) { struct tls *ctx; struct tls_ocsp_query *q; int ret; STACK_OF(OPENSSL_STRING) *ocsp_urls; ctx = tls_ocsp_client_new(); if (!ctx) return -1; *ocsp_ctx_p = ctx; q = ctx->ocsp_query; if (config) { /* create ctx->ssl_ctx */ ctx->flags = TLS_SERVER; ret = tls_configure(ctx, config); ctx->flags = TLS_OCSP_CLIENT; if (ret != 0) return ret; q->main_cert = SSL_get_certificate(ctx->ssl_conn); q->cert_ssl_ctx = ctx->ssl_ctx; SSL_CTX_get_extra_chain_certs(ctx->ssl_ctx, &q->extra_certs); } else { /* steal state from target struct */ q->main_cert = SSL_get_peer_certificate(target->ssl_conn); q->extra_certs = SSL_get_peer_cert_chain(target->ssl_conn); q->cert_ssl_ctx = target->ssl_ctx; X509_free(q->main_cert); /* unref */ } if (!q->main_cert) { tls_set_errorx(ctx, "No cert"); return -1; } ocsp_urls = X509_get1_ocsp(q->main_cert); if (!ocsp_urls) return TLS_NO_OCSP; q->ocsp_url = strdup(sk_OPENSSL_STRING_value(ocsp_urls, 0)); if (!q->ocsp_url) { tls_set_errorx(ctx, "Cannot copy URL"); goto failed; } ret = tls_build_ocsp_request(ctx); if (ret != 0) goto failed; *ocsp_ctx_p = ctx; failed: X509_email_free(ocsp_urls); return ret; } static int tls_ocsp_process_response_parsed(struct tls *ctx, struct tls_config *config, OCSP_RESPONSE *resp) { struct tls_ocsp_query *q = ctx->ocsp_query; BIO *mem = NULL; size_t len; unsigned char *data; int ret = -1, ok, res; res = tls_ocsp_verify_response(ctx, q->main_cert, q->extra_certs, q->cert_ssl_ctx, resp); if (res < 0) goto failed; /* Update blob in config */ if (config) { mem = BIO_new(BIO_s_mem()); if (!mem) { tls_set_error_libssl(ctx, "BIO_new"); goto failed; } ok = i2d_OCSP_RESPONSE_bio(mem, resp); if (!ok) { tls_set_error_libssl(ctx, "i2d_OCSP_RESPONSE_bio"); goto failed; } len = BIO_get_mem_data(mem, &data); res = tls_config_set_ocsp_stapling_mem(config, data, len); if (res < 0) goto failed; } ret = 0; failed: BIO_free(mem); tls_ocsp_fill_result(ctx, ret); return ret; } static int tls_ocsp_create_request(struct tls **ocsp_ctx_p, struct tls_config *config, struct tls *target, char **ocsp_url, void **request_blob, size_t *request_size) { int res; struct tls_ocsp_query *q; res = tls_ocsp_setup(ocsp_ctx_p, config, target); if (res != 0) return res; q = (*ocsp_ctx_p)->ocsp_query; *ocsp_url = q->ocsp_url; *request_blob = q->request_data; *request_size = q->request_size; return 0; } /* * Public API for request blobs. */ int tls_ocsp_check_peer_request(struct tls **ocsp_ctx_p, struct tls *target, char **ocsp_url, void **request_blob, size_t *request_size) { return tls_ocsp_create_request(ocsp_ctx_p, NULL, target, ocsp_url, request_blob, request_size); } int tls_ocsp_refresh_stapling_request(struct tls **ocsp_ctx_p, struct tls_config *config, char **ocsp_url, void **request_blob, size_t *request_size) { return tls_ocsp_create_request(ocsp_ctx_p, config, NULL, ocsp_url, request_blob, request_size); } int tls_ocsp_process_response(struct tls *ctx, const void *response_blob, size_t size) { int ret; OCSP_RESPONSE *resp; const unsigned char *raw = response_blob; resp = d2i_OCSP_RESPONSE(NULL, &raw, size); if (!resp) { ctx->ocsp_result = "parse-failed"; tls_set_error_libssl(ctx, "parse failed"); return -1; } ret = tls_ocsp_process_response_parsed(ctx, ctx->config, resp); OCSP_RESPONSE_free(resp); return ret; } /* * Network processing */ static int tls_ocsp_build_http_req(struct tls *ctx) { struct tls_ocsp_query *q = ctx->ocsp_query; int ok; OCSP_REQUEST *req; OCSP_REQ_CTX *sreq; const unsigned char *data; int ret=-1, https=0; char *host=NULL, *port=NULL, *path=NULL; ok = OCSP_parse_url(q->ocsp_url, &host, &port, &path, &https); if (ok != 1) { tls_set_error_libssl(ctx, "Cannot parse URL"); goto failed; } sreq = OCSP_sendreq_new(q->bio, path, NULL, -1); if (!sreq) { tls_set_error_libssl(ctx, "OCSP HTTP request setup failed"); goto failed; } q->http_req = sreq; ok = OCSP_REQ_CTX_add1_header(sreq, "Host", host); /* add payload after headers */ if (ok) { data = q->request_data; req = d2i_OCSP_REQUEST(NULL, &data, q->request_size); if (req) ok = OCSP_REQ_CTX_set1_req(sreq, req); else ok = false; OCSP_REQUEST_free(req); } if (ok) ret = 0; failed: free(host); free(port); free(path); return ret; } static int tls_ocsp_connection_setup(struct tls *ctx) { SSL *ssl = NULL; int ret = -1, ok, https=0; struct tls_ocsp_query *q = ctx->ocsp_query; union { struct in_addr ip4; struct in6_addr ip6; } addrbuf; char *host=NULL, *port=NULL, *path=NULL; ok = OCSP_parse_url(q->ocsp_url, &host, &port, &path, &https); if (ok != 1) { tls_set_error_libssl(ctx, "Cannot parse URL"); goto failed; } if (https) { q->ssl_ctx = SSL_CTX_new(SSLv23_client_method()); if (!q->ssl_ctx) { tls_set_error_libssl(ctx, "Cannot init SSL"); goto failed; } SSL_CTX_set_options(q->ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); SSL_CTX_clear_options(q->ssl_ctx, SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2); SSL_CTX_set_cipher_list(q->ssl_ctx, TLS_CIPHERS_COMPAT); SSL_CTX_set_mode(q->ssl_ctx, SSL_MODE_AUTO_RETRY); q->bio = BIO_new_ssl_connect(q->ssl_ctx); if (q->bio) { if (inet_pton(AF_INET, host, &addrbuf) != 1 && inet_pton(AF_INET6, host, &addrbuf) != 1) { if (!BIO_get_ssl(q->bio, &ssl)) { tls_set_errorx(ctx, "cannot get ssl struct"); goto failed; } if (SSL_set_tlsext_host_name(ssl, host) == 0) { tls_set_errorx(ctx, "server name indication failure"); goto failed; } } } } else { q->bio = BIO_new(BIO_s_connect()); } if (!q->bio) { tls_set_error_libssl(ctx, "Cannot connect"); goto failed; } BIO_set_conn_hostname(q->bio, host); BIO_set_conn_port(q->bio, port); BIO_set_nbio(q->bio, 1); ret = 0; failed: free(host); free(port); free(path); return ret; } static int tls_ocsp_evloop(struct tls *ctx, int *fd_p, struct tls_config *config) { struct tls_ocsp_query *q = ctx->ocsp_query; OCSP_RESPONSE *ocsp_resp = NULL; int ret, ok; if (q->http_req == NULL) { ok = BIO_do_connect(q->bio); if (ok != 1 && !BIO_should_retry(q->bio)) { tls_set_error_libssl(ctx, "Connection failure"); goto error; } *fd_p = BIO_get_fd(q->bio, NULL); if (*fd_p < 0) { tls_set_error_libssl(ctx, "Cannot get FD"); goto error; } if (ok != 1) return TLS_WANT_POLLOUT; ret = tls_ocsp_build_http_req(ctx); if (ret != 0) goto error; } ok = OCSP_sendreq_nbio(&ocsp_resp, q->http_req); if (ok == 1) { ret = tls_ocsp_process_response_parsed(ctx, config, ocsp_resp); return ret; } else if (ok == 0) { tls_set_error_libssl(ctx, "OCSP request failed"); goto error; } else if (BIO_should_read(q->bio)) { return TLS_WANT_POLLIN; } else if (BIO_should_write(q->bio)) { return TLS_WANT_POLLOUT; } tls_set_error_libssl(ctx, "Unexpected request error"); error: tls_ocsp_fill_result(ctx, -1); return -1; } static int tls_ocsp_do_poll(struct tls *ctx, int errcode, int fd) { struct pollfd pfd; int res; memset(&pfd, 0, sizeof(pfd)); pfd.fd = fd; if (errcode == TLS_WANT_POLLIN) { pfd.events = POLLIN; } else if (errcode == TLS_WANT_POLLOUT) { pfd.events = POLLOUT; } else { tls_set_error(ctx, "bad code to poll"); return -1; } res = poll(&pfd, 1, -1); if (res == 1) { return 0; } else if (res == 0) { tls_set_errorx(ctx, "poll timed out"); return -1; } tls_set_error(ctx, "poll error"); return -1; } static int tls_ocsp_query_async(struct tls **ocsp_ctx_p, int *fd_p, struct tls_config *config, struct tls *target) { struct tls *ctx = *ocsp_ctx_p; int ret; if (!ctx) { ret = tls_ocsp_setup(&ctx, config, target); if (ret != 0) goto failed; ret = tls_ocsp_connection_setup(ctx); if (ret != 0) goto failed; *ocsp_ctx_p = ctx; } return tls_ocsp_evloop(ctx, fd_p, config); failed: tls_free(ctx); return -1; } static int tls_ocsp_common_query(struct tls **ocsp_ctx_p, int *fd_p, struct tls_config *config, struct tls *target) { struct tls *ctx = NULL; int ret, fd; /* async path */ if (fd_p) return tls_ocsp_query_async(ocsp_ctx_p, fd_p, config, target); /* sync path */ while (1) { ret = tls_ocsp_query_async(&ctx, &fd, config, target); if (ret != TLS_WANT_POLLIN && ret != TLS_WANT_POLLOUT) break; ret = tls_ocsp_do_poll(ctx, ret, fd); if (ret != 0) break; } *ocsp_ctx_p = ctx; return ret; } /* * Public API. */ int tls_ocsp_check_peer(struct tls **ocsp_ctx_p, int *async_fd_p, struct tls *target) { return tls_ocsp_common_query(ocsp_ctx_p, async_fd_p, NULL, target); } int tls_ocsp_refresh_stapling(struct tls **ocsp_ctx_p, int *async_fd_p, struct tls_config *config) { return tls_ocsp_common_query(ocsp_ctx_p, async_fd_p, config, NULL); } #else /* No OCSP */ void tls_ocsp_info_free(struct tls_ocsp_info *info) {} void tls_ocsp_client_free(struct tls *ctx) {} int tls_get_ocsp_info(struct tls *ctx, int *response_status, int *cert_status, int *crl_reason, time_t *this_update, time_t *next_update, time_t *revoction_time, const char **result_text) { if (response_status) *response_status = -1; if (cert_status) *cert_status = -1; if (crl_reason) *crl_reason = -1; if (result_text) *result_text = "OCSP not supported"; if (this_update) *this_update = 0; if (next_update) *next_update = 0; if (revoction_time) *revoction_time = 0; return TLS_NO_OCSP; } int tls_ocsp_check_peer(struct tls **ocsp_ctx_p, int *async_fd_p, struct tls *target) { *ocsp_ctx_p = NULL; return TLS_NO_OCSP; } int tls_ocsp_refresh_stapling(struct tls **ocsp_ctx_p, int *async_fd_p, struct tls_config *config) { *ocsp_ctx_p = NULL; return TLS_NO_OCSP; } #endif /* OPENSSL_NO_OCSP */ #endif /* USUAL_LIBSSL_FOR_TLS */ pgbouncer-1.7/lib/usual/tls/tls_compat.c0000664000175000017500000003547712615705233015326 00000000000000/* * Compatibility with various libssl implementations. * * Copyright (c) 2015 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "tls_compat.h" #ifdef USUAL_LIBSSL_FOR_TLS #include #include #include #ifndef SSL_F_SSL_CTX_USE_CERTIFICATE_CHAIN_FILE #undef SSLerr #undef X509err #endif #ifndef SSLerr #define SSLerr(a,b) do {} while (0) #define X509err(a,b) do {} while (0) #endif #ifndef SSL_CTX_set_dh_auto /* * SKIP primes, used by OpenSSL and PostgreSQL. * * https://tools.ietf.org/html/draft-ietf-ipsec-skip-06 */ static const char file_dh1024[] = "-----BEGIN DH PARAMETERS-----\n" "MIGHAoGBAPSI/VhOSdvNILSd5JEHNmszbDgNRR0PfIizHHxbLY7288kjwEPwpVsY\n" "jY67VYy4XTjTNP18F1dDox0YbN4zISy1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6\n" "ypUM2Zafq9AKUJsCRtMIPWakXUGfnHy9iUsiGSa6q6Jew1XpL3jHAgEC\n" "-----END DH PARAMETERS-----\n"; static const char file_dh2048[] = "-----BEGIN DH PARAMETERS-----\n" "MIIBCAKCAQEA9kJXtwh/CBdyorrWqULzBej5UxE5T7bxbrlLOCDaAadWoxTpj0BV\n" "89AHxstDqZSt90xkhkn4DIO9ZekX1KHTUPj1WV/cdlJPPT2N286Z4VeSWc39uK50\n" "T8X8dryDxUcwYc58yWb/Ffm7/ZFexwGq01uejaClcjrUGvC/RgBYK+X0iP1YTknb\n" "zSC0neSRBzZrM2w4DUUdD3yIsxx8Wy2O9vPJI8BD8KVbGI2Ou1WMuF040zT9fBdX\n" "Q6MdGGzeMyEstSr/POGxKUAYEY18hKcKctaGxAMZyAcpesqVDNmWn6vQClCbAkbT\n" "CD1mpF1Bn5x8vYlLIhkmuquiXsNV6TILOwIBAg==\n" "-----END DH PARAMETERS-----\n"; static const char file_dh4096[] = "-----BEGIN DH PARAMETERS-----\n" "MIICCAKCAgEA+hRyUsFN4VpJ1O8JLcCo/VWr19k3BCgJ4uk+d+KhehjdRqNDNyOQ\n" "l/MOyQNQfWXPeGKmOmIig6Ev/nm6Nf9Z2B1h3R4hExf+zTiHnvVPeRBhjdQi81rt\n" "Xeoh6TNrSBIKIHfUJWBh3va0TxxjQIs6IZOLeVNRLMqzeylWqMf49HsIXqbcokUS\n" "Vt1BkvLdW48j8PPv5DsKRN3tloTxqDJGo9tKvj1Fuk74A+Xda1kNhB7KFlqMyN98\n" "VETEJ6c7KpfOo30mnK30wqw3S8OtaIR/maYX72tGOno2ehFDkq3pnPtEbD2CScxc\n" "alJC+EL7RPk5c/tgeTvCngvc1KZn92Y//EI7G9tPZtylj2b56sHtMftIoYJ9+ODM\n" "sccD5Piz/rejE3Ome8EOOceUSCYAhXn8b3qvxVI1ddd1pED6FHRhFvLrZxFvBEM9\n" "ERRMp5QqOaHJkM+Dxv8Cj6MqrCbfC4u+ZErxodzuusgDgvZiLF22uxMZbobFWyte\n" "OvOzKGtwcTqO/1wV5gKkzu1ZVswVUQd5Gg8lJicwqRWyyNRczDDoG9jVDxmogKTH\n" "AaqLulO7R8Ifa1SwF2DteSGVtgWEN8gDpN3RBmmPTDngyF2DHb5qmpnznwtFKdTL\n" "KWbuHn491xNO25CQWMtem80uKw+pTnisBRF/454n1Jnhub144YRBoN8CAQI=\n" "-----END DH PARAMETERS-----\n"; static DH *dh1024, *dh2048, *dh4096; static DH *load_dh_buffer(struct tls *ctx, DH **dhp, const char *buf) { BIO *bio; DH *dh = *dhp; if (dh == NULL) { bio = BIO_new_mem_buf((char *)buf, strlen(buf)); if (bio) { dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); BIO_free(bio); } *dhp = dh; } if (ctx) ctx->used_dh_bits = DH_size(dh) * 8; return dh; } static DH *dh_auto_cb(SSL *s, int is_export, int keylength) { EVP_PKEY *pk; int bits; struct tls *ctx = SSL_get_app_data(s); pk = SSL_get_privatekey(s); if (!pk) return load_dh_buffer(ctx, &dh2048, file_dh2048); bits = EVP_PKEY_bits(pk); if (bits >= 3072) return load_dh_buffer(ctx, &dh4096, file_dh4096); if (bits >= 1536) return load_dh_buffer(ctx, &dh2048, file_dh2048); return load_dh_buffer(ctx, &dh1024, file_dh1024); } static DH *dh_legacy_cb(SSL *s, int is_export, int keylength) { struct tls *ctx = SSL_get_app_data(s); return load_dh_buffer(ctx, &dh1024, file_dh1024); } long SSL_CTX_set_dh_auto(SSL_CTX *ctx, int onoff) { if (onoff == 0) return 1; if (onoff == 2) { SSL_CTX_set_tmp_dh_callback(ctx, dh_legacy_cb); } else { SSL_CTX_set_tmp_dh_callback(ctx, dh_auto_cb); } SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE); return 1; } #endif #ifndef SSL_CTX_set_ecdh_auto /* * Use same curve as EC key, fallback to NIST P-256. */ static EC_KEY *ecdh_auto_cb(SSL *ssl, int is_export, int keylength) { static EC_KEY *ecdh; int last_nid; int nid = 0; EVP_PKEY *pk; EC_KEY *ec; struct tls *ctx = SSL_get_app_data(ssl); /* pick curve from EC key */ pk = SSL_get_privatekey(ssl); if (pk && pk->type == EVP_PKEY_EC) { ec = EVP_PKEY_get1_EC_KEY(pk); if (ec) { nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec)); EC_KEY_free(ec); } } /* ssl->tlsext_ellipticcurvelist is empty, nothing else to do... */ if (nid == 0) nid = NID_X9_62_prime256v1; if (ctx) ctx->used_ecdh_nid = nid; if (ecdh) { last_nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ecdh)); if (last_nid == nid) return ecdh; EC_KEY_free(ecdh); ecdh = NULL; } ecdh = EC_KEY_new_by_curve_name(nid); return ecdh; } long SSL_CTX_set_ecdh_auto(SSL_CTX *ctx, int onoff) { if (onoff) { SSL_CTX_set_options(ctx, SSL_OP_SINGLE_ECDH_USE); SSL_CTX_set_tmp_ecdh_callback(ctx, ecdh_auto_cb); } return 1; } #endif #ifndef HAVE_SSL_CTX_USE_CERTIFICATE_CHAIN_MEM /* * Load certs for public key from memory. */ int SSL_CTX_use_certificate_chain_mem(SSL_CTX *ctx, void *data, int data_len) { pem_password_cb *psw_fn = NULL; void *psw_arg = NULL; X509 *cert; BIO *bio = NULL; int ok; #ifdef USE_LIBSSL_INTERNALS psw_fn = ctx->default_passwd_callback; psw_arg = ctx->default_passwd_callback_userdata; #endif ERR_clear_error(); /* Read from memory */ bio = BIO_new_mem_buf(data, data_len); if (!bio) { SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_CHAIN_FILE, ERR_R_BUF_LIB); goto failed; } /* Load primary cert */ cert = PEM_read_bio_X509_AUX(bio, NULL, psw_fn, psw_arg); if (!cert) { SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_CHAIN_FILE, ERR_R_PEM_LIB); goto failed; } /* Increments refcount */ ok = SSL_CTX_use_certificate(ctx, cert); X509_free(cert); if (!ok || ERR_peek_error()) goto failed; /* Load extra certs */ ok = SSL_CTX_clear_extra_chain_certs(ctx); while (ok) { cert = PEM_read_bio_X509(bio, NULL, psw_fn, psw_arg); if (!cert) { /* Is it EOF? */ unsigned long err = ERR_peek_last_error(); if (ERR_GET_LIB(err) != ERR_LIB_PEM) break; if (ERR_GET_REASON(err) != PEM_R_NO_START_LINE) break; /* On EOF do successful exit */ BIO_free(bio); ERR_clear_error(); return 1; } /* Does not increment refcount */ ok = SSL_CTX_add_extra_chain_cert(ctx, cert); if (!ok) X509_free(cert); } failed: if (bio) BIO_free(bio); return 0; } #endif #ifndef HAVE_SSL_CTX_LOAD_VERIFY_MEM /* * Load CA certs for verification from memory. */ int SSL_CTX_load_verify_mem(SSL_CTX *ctx, void *data, int data_len) { STACK_OF(X509_INFO) *stack = NULL; X509_STORE *store; X509_INFO *info; int nstack, i, ret = 0, got = 0; BIO *bio; /* Read from memory */ bio = BIO_new_mem_buf(data, data_len); if (!bio) goto failed; /* Parse X509_INFO records */ stack = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL); if (!stack) goto failed; /* Loop over stack, add certs and revocation records to store */ store = SSL_CTX_get_cert_store(ctx); nstack = sk_X509_INFO_num(stack); for (i = 0; i < nstack; i++) { info = sk_X509_INFO_value(stack, i); if (info->x509 && !X509_STORE_add_cert(store, info->x509)) goto failed; if (info->crl && !X509_STORE_add_crl(store, info->crl)) goto failed; if (info->x509 || info->crl) got = 1; } ret = got; failed: if (bio) BIO_free(bio); if (stack) sk_X509_INFO_pop_free(stack, X509_INFO_free); if (!ret) X509err(X509_F_X509_LOAD_CERT_CRL_FILE, ERR_R_PEM_LIB); return ret; } #endif #ifndef HAVE_ASN1_TIME_PARSE static int parse2num(const char **str_p, int min, int max) { const char *s = *str_p; if (s && s[0] >= '0' && s[0] <= '9' && s[1] >= '0' && s[1] <= '9') { int val = (s[0] - '0') * 10 + (s[1] - '0'); if (val >= min && val <= max) { *str_p += 2; return val; } } *str_p = NULL; return 0; } int asn1_time_parse(const char *src, size_t len, struct tm *tm, int mode) { char buf[16]; const char *s = buf; int utctime; int year; memset(tm, 0, sizeof tm); if (mode != 0) return -1; /* * gentime: YYYYMMDDHHMMSSZ * utctime: YYMMDDHHMM(SS)(Z) */ if (len == 15) { utctime = 0; } else if (len > 8 && len < 15) { utctime = 1; } else { return -1; } memcpy(buf, src, len); buf[len] = '\0'; year = parse2num(&s, 0, 99); if (utctime) { if (year < 50) year = 2000 + year; else year = 1900 + year; } else { year = year*100 + parse2num(&s, 0, 99); } tm->tm_year = year - 1900; tm->tm_mon = parse2num(&s, 1, 12) - 1; tm->tm_mday = parse2num(&s, 1, 31); tm->tm_hour = parse2num(&s, 0, 23); tm->tm_min = parse2num(&s, 0, 59); if (utctime) { if (s && s[0] != 'Z' && s[0] != '\0') tm->tm_sec = parse2num(&s, 0, 61); } else { tm->tm_sec = parse2num(&s, 0, 61); } if (s) { if (s[0] == '\0') goto good; if (s[0] == 'Z' && s[1] == '\0') goto good; } return -1; good: return 0; } #endif /* HAVE_ASN1_TIME_PARSE */ int tls_asn1_parse_time(struct tls *ctx, const ASN1_TIME *asn1time, time_t *dst) { struct tm tm; int res; time_t tval; *dst = 0; if (!asn1time) return 0; if (asn1time->type != V_ASN1_GENERALIZEDTIME && asn1time->type != V_ASN1_UTCTIME) { tls_set_errorx(ctx, "Invalid time object type: %d", asn1time->type); return -1; } res = asn1_time_parse((char*)asn1time->data, asn1time->length, &tm, 0); if (res != 0) { tls_set_errorx(ctx, "Invalid asn1 time"); return -1; } tval = timegm(&tm); if (tval == (time_t)-1) { tls_set_error(ctx, "Cannot convert asn1 time"); return -1; } *dst = tval; return 0; } #else /* !USUAL_LIBSSL_FOR_TLS */ #include /* * Install empty functions when openssl is not available. */ int tls_init(void) { return -1; } const char *tls_error(struct tls *_ctx) { return "No TLS support"; } struct tls_config *tls_config_new(void) { return NULL; } void tls_config_free(struct tls_config *_config) {} int tls_config_set_ca_file(struct tls_config *_config, const char *_ca_file) { return -1; } int tls_config_set_ca_path(struct tls_config *_config, const char *_ca_path) { return -1; } int tls_config_set_ca_mem(struct tls_config *_config, const uint8_t *_ca, size_t _len) { return -1; } int tls_config_set_cert_file(struct tls_config *_config, const char *_cert_file) { return -1; } int tls_config_set_cert_mem(struct tls_config *_config, const uint8_t *_cert, size_t _len) { return -1; } int tls_config_set_ciphers(struct tls_config *_config, const char *_ciphers) { return -1; } int tls_config_set_dheparams(struct tls_config *_config, const char *_params) { return -1; } int tls_config_set_ecdhecurve(struct tls_config *_config, const char *_name) { return -1; } int tls_config_set_key_file(struct tls_config *_config, const char *_key_file) { return -1; } int tls_config_set_key_mem(struct tls_config *_config, const uint8_t *_key, size_t _len) { return -1; } int tls_config_set_ocsp_stapling_file(struct tls_config *_config, const char *_blob_file) { return -1; } int tls_config_set_ocsp_stapling_mem(struct tls_config *_config, const uint8_t *_blob, size_t _len) { return -1; } void tls_config_set_protocols(struct tls_config *_config, uint32_t _protocols) {} void tls_config_set_verify_depth(struct tls_config *_config, int _verify_depth) {} void tls_config_prefer_ciphers_client(struct tls_config *_config) {} void tls_config_prefer_ciphers_server(struct tls_config *_config) {} void tls_config_insecure_noverifycert(struct tls_config *_config) {} void tls_config_insecure_noverifyname(struct tls_config *_config) {} void tls_config_insecure_noverifytime(struct tls_config *_config) {} void tls_config_verify(struct tls_config *_config) {} void tls_config_verify_client(struct tls_config *_config) {} void tls_config_verify_client_optional(struct tls_config *_config) {} void tls_config_clear_keys(struct tls_config *_config) {} int tls_config_parse_protocols(uint32_t *_protocols, const char *_protostr) { return -1; } struct tls *tls_client(void) { return NULL; } struct tls *tls_server(void) { return NULL; } int tls_configure(struct tls *_ctx, struct tls_config *_config) { return -1; } void tls_reset(struct tls *_ctx) {} void tls_free(struct tls *_ctx) {} int tls_accept_fds(struct tls *_ctx, struct tls **_cctx, int _fd_read, int _fd_write) { return -1; } int tls_accept_socket(struct tls *_ctx, struct tls **_cctx, int _socket) { return -1; } int tls_connect(struct tls *_ctx, const char *_host, const char *_port) { return -1; } int tls_connect_fds(struct tls *_ctx, int _fd_read, int _fd_write, const char *_servername) { return -1; } int tls_connect_servername(struct tls *_ctx, const char *_host, const char *_port, const char *_servername) { return -1; } int tls_connect_socket(struct tls *_ctx, int _s, const char *_servername) { return -1; } int tls_handshake(struct tls *_ctx) { return -1; } ssize_t tls_read(struct tls *_ctx, void *_buf, size_t _buflen) { return -1; } ssize_t tls_write(struct tls *_ctx, const void *_buf, size_t _buflen) { return -1; } int tls_close(struct tls *_ctx) { return -1; } int tls_peer_cert_provided(struct tls *ctx) { return 0; } int tls_peer_cert_contains_name(struct tls *ctx, const char *name) { return 0; } const char *tls_peer_cert_hash(struct tls *_ctx) { return NULL; } const char *tls_peer_cert_issuer(struct tls *ctx) { return NULL; } const char *tls_peer_cert_subject(struct tls *ctx) { return NULL; } time_t tls_peer_cert_notbefore(struct tls *ctx) { return (time_t)-1; } time_t tls_peer_cert_notafter(struct tls *ctx) { return (time_t)-1; } const char *tls_conn_version(struct tls *ctx) { return "n/a"; } const char *tls_conn_cipher(struct tls *ctx) { return "n/a"; } uint8_t *tls_load_file(const char *_file, size_t *_len, char *_password) { return NULL; } ssize_t tls_get_connection_info(struct tls *ctx, char *buf, size_t buflen) { return -1; } int tls_ocsp_refresh_stapling(struct tls **ocsp_ctx_p, int *async_fd_p, struct tls_config *config) { return -1; } int tls_ocsp_check_peer(struct tls **ocsp_ctx_p, int *async_fd_p, struct tls *client) { return -1; } int tls_get_ocsp_info(struct tls *ctx, int *response_status, int *cert_status, int *crl_reason, time_t *this_update, time_t *next_update, time_t *revoction_time, const char **result_text) { return -1; } int tls_ocsp_check_peer_request(struct tls **ocsp_ctx_p, struct tls *target, char **ocsp_url, void **request_blob, size_t *request_size) { return -1; } int tls_ocsp_refresh_stapling_request(struct tls **ocsp_ctx_p, struct tls_config *config, char **ocsp_url, void **request_blob, size_t *request_size) { return -1; } int tls_get_peer_cert(struct tls *ctx, struct tls_cert **cert_p, const char *algo) { *cert_p = NULL; return -1; } void tls_cert_free(struct tls_cert *cert) {} #endif /* !USUAL_LIBSSL_FOR_TLS */ pgbouncer-1.7/lib/usual/tls/tls.c0000664000175000017500000003141712615704017013750 00000000000000/* $OpenBSD: tls.c,v 1.11 2015/04/15 16:08:43 jsing Exp $ */ /* * Copyright (c) 2014 Joel Sing * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "tls_compat.h" #ifdef USUAL_LIBSSL_FOR_TLS #include #include #include #include #include #include #include #include "tls_internal.h" static struct tls_config *tls_config_default; int tls_init(void) { static int tls_initialised = 0; if (tls_initialised) return (0); SSL_load_error_strings(); SSL_library_init(); if ((tls_config_default = tls_config_new()) == NULL) return (-1); tls_initialised = 1; return (0); } const char * tls_error(struct tls *ctx) { return ctx->errmsg; } static int tls_set_verror(struct tls *ctx, int errnum, const char *fmt, va_list ap) { char *errmsg = NULL; int rv = -1; free(ctx->errmsg); ctx->errmsg = NULL; if (vasprintf(&errmsg, fmt, ap) == -1) { errmsg = NULL; goto err; } if (errnum == -1) { ctx->errmsg = errmsg; return (0); } if (asprintf(&ctx->errmsg, "%s: %s", errmsg, strerror(errnum)) == -1) { ctx->errmsg = NULL; goto err; } rv = 0; err: free(errmsg); return (rv); } int tls_set_error(struct tls *ctx, const char *fmt, ...) { va_list ap; int rv; ctx->errnum = errno; va_start(ap, fmt); rv = tls_set_verror(ctx, ctx->errnum, fmt, ap); va_end(ap); return (rv); } int tls_set_errorx(struct tls *ctx, const char *fmt, ...) { va_list ap; int rv; va_start(ap, fmt); rv = tls_set_verror(ctx, -1, fmt, ap); va_end(ap); return (rv); } int tls_set_error_libssl(struct tls *ctx, const char *fmt, ...) { va_list ap; int rv; const char *msg = NULL; char *old; int err; err = ERR_peek_error(); if (err != 0) msg = ERR_reason_error_string(err); va_start(ap, fmt); rv = tls_set_verror(ctx, -1, fmt, ap); va_end(ap); if (rv != 0 || msg == NULL) return rv; old = ctx->errmsg; ctx->errmsg = NULL; if (asprintf(&ctx->errmsg, "%s: %s", old, msg) == -1) { ctx->errmsg = old; return 0; } free(old); return 0; } struct tls * tls_new(void) { struct tls *ctx; if ((ctx = calloc(1, sizeof(*ctx))) == NULL) return (NULL); ctx->config = tls_config_default; tls_reset(ctx); return (ctx); } int tls_configure(struct tls *ctx, struct tls_config *config) { if (config == NULL) config = tls_config_default; ctx->config = config; if ((ctx->flags & TLS_SERVER) != 0) return (tls_configure_server(ctx)); return (0); } int tls_configure_keypair(struct tls *ctx, int required) { EVP_PKEY *pkey = NULL; X509 *cert = NULL; BIO *bio = NULL; if (!required && ctx->config->cert_mem == NULL && ctx->config->key_mem == NULL && ctx->config->cert_file == NULL && ctx->config->key_file == NULL) return(0); if (ctx->config->cert_mem != NULL) { if (ctx->config->cert_len > INT_MAX) { tls_set_errorx(ctx, "certificate too long"); goto err; } if (SSL_CTX_use_certificate_chain_mem(ctx->ssl_ctx, ctx->config->cert_mem, ctx->config->cert_len) != 1) { tls_set_errorx(ctx, "failed to load certificate"); goto err; } cert = NULL; } if (ctx->config->key_mem != NULL) { if (ctx->config->key_len > INT_MAX) { tls_set_errorx(ctx, "key too long"); goto err; } if ((bio = BIO_new_mem_buf(ctx->config->key_mem, ctx->config->key_len)) == NULL) { tls_set_errorx(ctx, "failed to create buffer"); goto err; } if ((pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL)) == NULL) { tls_set_errorx(ctx, "failed to read private key"); goto err; } if (SSL_CTX_use_PrivateKey(ctx->ssl_ctx, pkey) != 1) { tls_set_errorx(ctx, "failed to load private key"); goto err; } BIO_free(bio); bio = NULL; EVP_PKEY_free(pkey); pkey = NULL; } if (ctx->config->cert_file != NULL) { if (SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx, ctx->config->cert_file) != 1) { tls_set_errorx(ctx, "failed to load certificate file"); goto err; } } if (ctx->config->key_file != NULL) { if (SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, ctx->config->key_file, SSL_FILETYPE_PEM) != 1) { tls_set_errorx(ctx, "failed to load private key file"); goto err; } } if (SSL_CTX_check_private_key(ctx->ssl_ctx) != 1) { tls_set_errorx(ctx, "private/public key mismatch"); goto err; } return (0); err: EVP_PKEY_free(pkey); X509_free(cert); BIO_free(bio); return (1); } static void tls_info_callback(const SSL *ssl, int where, int rc) { struct tls *ctx = SSL_get_app_data(ssl); #ifdef USE_LIBSSL_INTERNALS if (!(ctx->state & TLS_HANDSHAKE_COMPLETE) && ssl->s3) { /* steal info about used DH key */ if (ssl->s3->tmp.dh && !ctx->used_dh_bits) { ctx->used_dh_bits = DH_size(ssl->s3->tmp.dh) * 8; } else if (ssl->s3->tmp.ecdh && !ctx->used_ecdh_nid) { ctx->used_ecdh_nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ssl->s3->tmp.ecdh)); } } #endif /* detect renegotation on established connection */ if (where & SSL_CB_HANDSHAKE_START) { if (ctx->state & TLS_HANDSHAKE_COMPLETE) ctx->state |= TLS_DO_ABORT; } } static int tls_do_abort(struct tls *ctx) { int ssl_ret, rv; ssl_ret = SSL_shutdown(ctx->ssl_conn); if (ssl_ret < 0) { rv = tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "shutdown"); if (rv == TLS_WANT_POLLIN || rv == TLS_WANT_POLLOUT) return (rv); } tls_set_errorx(ctx, "unexpected handshake, closing connection"); return -1; } int tls_configure_ssl(struct tls *ctx) { SSL_CTX_set_mode(ctx->ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE); SSL_CTX_set_mode(ctx->ssl_ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv2); SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv3); SSL_CTX_clear_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1); SSL_CTX_clear_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1_1); SSL_CTX_clear_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1_2); if ((ctx->config->protocols & TLS_PROTOCOL_TLSv1_0) == 0) SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1); if ((ctx->config->protocols & TLS_PROTOCOL_TLSv1_1) == 0) SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1_1); if ((ctx->config->protocols & TLS_PROTOCOL_TLSv1_2) == 0) SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1_2); if (ctx->config->ciphers != NULL) { if (SSL_CTX_set_cipher_list(ctx->ssl_ctx, ctx->config->ciphers) != 1) { tls_set_errorx(ctx, "failed to set ciphers"); goto err; } } SSL_CTX_set_info_callback(ctx->ssl_ctx, tls_info_callback); #ifdef X509_V_FLAG_NO_CHECK_TIME if (ctx->config->verify_time == 0) { X509_VERIFY_PARAM *vfp = SSL_CTX_get0_param(ctx->ssl_ctx); X509_VERIFY_PARAM_set_flags(vfp, X509_V_FLAG_NO_CHECK_TIME); } #endif return (0); err: return (-1); } int tls_configure_ssl_verify(struct tls *ctx, int verify) { SSL_CTX_set_verify(ctx->ssl_ctx, verify, NULL); if (ctx->config->ca_mem != NULL) { /* XXX do this in set. */ if (ctx->config->ca_len > INT_MAX) { tls_set_errorx(ctx, "ca too long"); goto err; } if (SSL_CTX_load_verify_mem(ctx->ssl_ctx, ctx->config->ca_mem, ctx->config->ca_len) != 1) { tls_set_errorx(ctx, "ssl verify memory setup failure"); goto err; } } else if (SSL_CTX_load_verify_locations(ctx->ssl_ctx, ctx->config->ca_file, ctx->config->ca_path) != 1) { tls_set_errorx(ctx, "ssl verify setup failure"); goto err; } if (ctx->config->verify_depth >= 0) SSL_CTX_set_verify_depth(ctx->ssl_ctx, ctx->config->verify_depth); return (0); err: return (-1); } void tls_free(struct tls *ctx) { if (ctx == NULL) return; tls_reset(ctx); free(ctx); } void tls_reset(struct tls *ctx) { SSL_CTX_free(ctx->ssl_ctx); SSL_free(ctx->ssl_conn); X509_free(ctx->ssl_peer_cert); ctx->ssl_conn = NULL; ctx->ssl_ctx = NULL; ctx->ssl_peer_cert = NULL; ctx->socket = -1; ctx->state = 0; free(ctx->servername); ctx->servername = NULL; free(ctx->errmsg); ctx->errmsg = NULL; ctx->errnum = 0; tls_free_conninfo(ctx->conninfo); free(ctx->conninfo); ctx->conninfo = NULL; ctx->used_dh_bits = 0; ctx->used_ecdh_nid = 0; tls_ocsp_info_free(ctx->ocsp_info); ctx->ocsp_info = NULL; ctx->ocsp_result = NULL; if (ctx->flags & TLS_OCSP_CLIENT) tls_ocsp_client_free(ctx); } int tls_ssl_error(struct tls *ctx, SSL *ssl_conn, int ssl_ret, const char *prefix) { const char *errstr = "unknown error"; unsigned long err; int ssl_err; ssl_err = SSL_get_error(ssl_conn, ssl_ret); switch (ssl_err) { case SSL_ERROR_NONE: case SSL_ERROR_ZERO_RETURN: return (0); case SSL_ERROR_WANT_READ: return (TLS_WANT_POLLIN); case SSL_ERROR_WANT_WRITE: return (TLS_WANT_POLLOUT); case SSL_ERROR_SYSCALL: if ((err = ERR_peek_error()) != 0) { errstr = ERR_error_string(err, NULL); } else if (ssl_ret == 0) { ctx->state |= TLS_EOF_NO_CLOSE_NOTIFY; return (0); } else if (ssl_ret == -1) { errstr = strerror(errno); } tls_set_errorx(ctx, "%s failed: %s", prefix, errstr); return (-1); case SSL_ERROR_SSL: if ((err = ERR_peek_error()) != 0) { errstr = ERR_error_string(err, NULL); } tls_set_errorx(ctx, "%s failed: %s", prefix, errstr); return (-1); case SSL_ERROR_WANT_CONNECT: case SSL_ERROR_WANT_ACCEPT: case SSL_ERROR_WANT_X509_LOOKUP: default: tls_set_errorx(ctx, "%s failed (%i)", prefix, ssl_err); return (-1); } } int tls_handshake(struct tls *ctx) { int rv = -1; if ((ctx->flags & (TLS_CLIENT | TLS_SERVER_CONN)) == 0) { tls_set_errorx(ctx, "invalid operation for context"); goto out; } if (ctx->conninfo == NULL && (ctx->conninfo = calloc(1, sizeof(*ctx->conninfo))) == NULL) goto out; if ((ctx->flags & TLS_CLIENT) != 0) rv = tls_handshake_client(ctx); else if ((ctx->flags & TLS_SERVER_CONN) != 0) rv = tls_handshake_server(ctx); if (rv == 0) { ctx->ssl_peer_cert = SSL_get_peer_certificate(ctx->ssl_conn); if (tls_get_conninfo(ctx) == -1) rv = -1; } out: /* Prevent callers from performing incorrect error handling */ errno = 0; return (rv); } ssize_t tls_read(struct tls *ctx, void *buf, size_t buflen) { ssize_t rv = -1; int ssl_ret; if (ctx->state & TLS_DO_ABORT) { rv = tls_do_abort(ctx); goto out; } if ((ctx->state & TLS_HANDSHAKE_COMPLETE) == 0) { if ((rv = tls_handshake(ctx)) != 0) goto out; } if (buflen > INT_MAX) { tls_set_errorx(ctx, "buflen too long"); goto out; } ERR_clear_error(); if ((ssl_ret = SSL_read(ctx->ssl_conn, buf, buflen)) > 0) { rv = (ssize_t)ssl_ret; goto out; } rv = (ssize_t)tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "read"); out: /* Prevent callers from performing incorrect error handling */ errno = 0; return (rv); } ssize_t tls_write(struct tls *ctx, const void *buf, size_t buflen) { ssize_t rv = -1; int ssl_ret; if (ctx->state & TLS_DO_ABORT) { rv = tls_do_abort(ctx); goto out; } if ((ctx->state & TLS_HANDSHAKE_COMPLETE) == 0) { if ((rv = tls_handshake(ctx)) != 0) goto out; } if (buflen > INT_MAX) { tls_set_errorx(ctx, "buflen too long"); goto out; } ERR_clear_error(); if ((ssl_ret = SSL_write(ctx->ssl_conn, buf, buflen)) > 0) { rv = (ssize_t)ssl_ret; goto out; } rv = (ssize_t)tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "write"); out: /* Prevent callers from performing incorrect error handling */ errno = 0; return (rv); } int tls_close(struct tls *ctx) { int ssl_ret; int rv = 0; if ((ctx->flags & (TLS_CLIENT | TLS_SERVER_CONN)) == 0) { tls_set_errorx(ctx, "invalid operation for context"); rv = -1; goto out; } if (ctx->ssl_conn != NULL) { ERR_clear_error(); ssl_ret = SSL_shutdown(ctx->ssl_conn); if (ssl_ret < 0) { rv = tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "shutdown"); if (rv == TLS_WANT_POLLIN || rv == TLS_WANT_POLLOUT) goto out; } } if (ctx->socket != -1) { if (shutdown(ctx->socket, SHUT_RDWR) != 0) { if (rv == 0 && errno != ENOTCONN && errno != ECONNRESET) { tls_set_error(ctx, "shutdown"); rv = -1; } } if (close(ctx->socket) != 0) { if (rv == 0) { tls_set_error(ctx, "close"); rv = -1; } } ctx->socket = -1; } if ((ctx->state & TLS_EOF_NO_CLOSE_NOTIFY) != 0) { tls_set_errorx(ctx, "EOF without close notify"); rv = -1; } out: /* Prevent callers from performing incorrect error handling */ errno = 0; return (rv); } #endif /* USUAL_LIBSSL_FOR_TLS */ pgbouncer-1.7/lib/usual/tls/tls_peer.c0000664000175000017500000000401612615705233014757 00000000000000/* $OpenBSD: tls_peer.c,v 1.3 2015/09/11 13:22:39 beck Exp $ */ /* * Copyright (c) 2015 Joel Sing * Copyright (c) 2015 Bob Beck * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "tls_compat.h" #ifdef USUAL_LIBSSL_FOR_TLS #include #include #include "tls_internal.h" const char * tls_peer_cert_hash(struct tls *ctx) { if (ctx->conninfo) return (ctx->conninfo->hash); return NULL; } const char * tls_peer_cert_issuer(struct tls *ctx) { if (ctx->conninfo) return (ctx->conninfo->issuer); return NULL; } const char * tls_peer_cert_subject(struct tls *ctx) { if (ctx->conninfo) return (ctx->conninfo->subject); return NULL; } int tls_peer_cert_provided(struct tls *ctx) { return (ctx->ssl_peer_cert != NULL); } int tls_peer_cert_contains_name(struct tls *ctx, const char *name) { if (ctx->ssl_peer_cert == NULL) return (0); return (tls_check_name(ctx, ctx->ssl_peer_cert, name) == 0); } time_t tls_peer_cert_notbefore(struct tls *ctx) { if (ctx->ssl_peer_cert == NULL) return (-1); if (ctx->conninfo == NULL) return (-1); return (ctx->conninfo->notbefore); } time_t tls_peer_cert_notafter(struct tls *ctx) { if (ctx->ssl_peer_cert == NULL) return (-1); if (ctx->conninfo == NULL) return (-1); return (ctx->conninfo->notafter); } #endif /* USUAL_LIBSSL_FOR_TLS */ pgbouncer-1.7/lib/usual/tls/tls_cert.h0000664000175000017500000000477412576101475015005 00000000000000#ifndef _USUAL_TLS_TLS_CERT_H_ #define _USUAL_TLS_TLS_CERT_H_ #define TLS_CERT_GNAME_DNS 1 #define TLS_CERT_GNAME_IPv4 2 #define TLS_CERT_GNAME_IPv6 3 #define TLS_CERT_GNAME_EMAIL 4 #define TLS_CERT_GNAME_URI 5 #define TLS_KU_DIGITAL_SIGNATURE (1 << 0) #define TLS_KU_NON_REPUDIATION (1 << 1) #define TLS_KU_KEY_ENCIPHERMENT (1 << 2) #define TLS_KU_DATA_ENCIPHERMENT (1 << 3) #define TLS_KU_KEY_AGREEMENT (1 << 4) #define TLS_KU_KEY_CERT_SIGN (1 << 5) #define TLS_KU_CRL_SIGN (1 << 6) #define TLS_KU_ENCIPHER_ONLY (1 << 7) #define TLS_KU_DECIPHER_ONLY (1 << 8) #define TLS_XKU_SSL_SERVER (1 << 0) #define TLS_XKU_SSL_CLIENT (1 << 1) #define TLS_XKU_SMIME (1 << 2) #define TLS_XKU_CODE_SIGN (1 << 3) #define TLS_XKU_OCSP_SIGN (1 << 4) #define TLS_XKU_SGC (1 << 5) #define TLS_XKU_TIMESTAMP (1 << 6) #define TLS_XKU_DVCS (1 << 7) #define TLS_EXT_BASIC (1 << 0) #define TLS_EXT_KEY_USAGE (1 << 1) #define TLS_EXT_EXTENDED_KEY_USAGE (1 << 2) #define TLS_EXT_SUBJECT_ALT_NAME (1 << 3) /* * GeneralName */ struct tls_cert_general_name { const void *name_value; int name_type; }; /* * DistinguishedName */ struct tls_cert_dname { const char *common_name; const char *country_name; const char *state_or_province_name; const char *locality_name; const char *street_address; const char *organization_name; const char *organizational_unit_name; }; struct tls_cert { /* Version number from cert: 0:v1, 1:v2, 2:v3 */ int version; /* did it pass verify? useful when noverifycert is on. */ int successful_verify; /* DistringuishedName for subject */ struct tls_cert_dname subject; /* DistringuishedName for issuer */ struct tls_cert_dname issuer; /* decimal number */ const char *serial; /* Validity times */ time_t not_before; time_t not_after; uint32_t ext_set; uint32_t ext_crit; /* BasicConstraints extension */ int basic_constraints_ca; int basic_constraints_pathlen; /* KeyUsage extension */ uint32_t key_usage_flags; /* ExtendedKeyUsage extension */ uint32_t extended_key_usage_flags; /* SubjectAltName extension */ struct tls_cert_general_name *subject_alt_names; int subject_alt_name_count; /* Fingerprint as raw hash */ const unsigned char *fingerprint; size_t fingerprint_size; }; int tls_get_peer_cert(struct tls *ctx, struct tls_cert **cert_p, const char *fingerprint_algo); void tls_cert_free(struct tls_cert *cert); #ifdef TLS_CERT_INTERNAL_FUNCS int tls_parse_cert(struct tls *ctx, struct tls_cert **cert_p, const char *fingerprint_algo, X509 *x509); #endif #endif pgbouncer-1.7/lib/usual/tls/tls_client.c0000664000175000017500000001541212615704017015303 00000000000000/* $OpenBSD: tls_client.c,v 1.16 2015/03/21 15:35:15 sthen Exp $ */ /* * Copyright (c) 2014 Joel Sing * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "tls_compat.h" #ifdef USUAL_LIBSSL_FOR_TLS #include #include #include #include #include "tls_internal.h" struct tls * tls_client(void) { struct tls *ctx; if ((ctx = tls_new()) == NULL) return (NULL); ctx->flags |= TLS_CLIENT; return (ctx); } int tls_connect(struct tls *ctx, const char *host, const char *port) { return tls_connect_servername(ctx, host, port, NULL); } int tls_connect_servername(struct tls *ctx, const char *host, const char *port, const char *servername) { struct addrinfo hints, *res, *res0; const char *h = NULL, *p = NULL; char *hs = NULL, *ps = NULL; int rv = -1, s = -1, ret; if ((ctx->flags & TLS_CLIENT) == 0) { tls_set_errorx(ctx, "not a client context"); goto err; } if (host == NULL) { tls_set_errorx(ctx, "host not specified"); goto err; } /* * If port is NULL try to extract a port from the specified host, * otherwise use the default. */ if ((p = (char *)port) == NULL) { ret = tls_host_port(host, &hs, &ps); if (ret == -1) { tls_set_errorx(ctx, "memory allocation failure"); goto err; } if (ret != 0) { tls_set_errorx(ctx, "no port provided"); goto err; } } h = (hs != NULL) ? hs : host; p = (ps != NULL) ? ps : port; /* * First check if the host is specified as a numeric IP address, * either IPv4 or IPv6, before trying to resolve the host. * The AI_ADDRCONFIG resolver option will not return IPv4 or IPv6 * records if it is not configured on an interface; not considering * loopback addresses. Checking the numeric addresses first makes * sure that connection attempts to numeric addresses and especially * 127.0.0.1 or ::1 loopback addresses are always possible. */ memset(&hints, 0, sizeof(hints)); hints.ai_socktype = SOCK_STREAM; /* try as an IPv4 literal */ hints.ai_family = AF_INET; hints.ai_flags = AI_NUMERICHOST; if (getaddrinfo(h, p, &hints, &res0) != 0) { /* try again as an IPv6 literal */ hints.ai_family = AF_INET6; if (getaddrinfo(h, p, &hints, &res0) != 0) { /* last try, with name resolution and save the error */ hints.ai_family = AF_UNSPEC; hints.ai_flags = AI_ADDRCONFIG; if ((s = getaddrinfo(h, p, &hints, &res0)) != 0) { tls_set_error(ctx, "%s", gai_strerror(s)); goto err; } } } /* It was resolved somehow; now try connecting to what we got */ s = -1; for (res = res0; res; res = res->ai_next) { s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (s == -1) { tls_set_error(ctx, "socket"); continue; } if (connect(s, res->ai_addr, res->ai_addrlen) == -1) { tls_set_error(ctx, "connect"); close(s); s = -1; continue; } break; /* Connected. */ } freeaddrinfo(res0); if (s == -1) goto err; if (servername == NULL) servername = h; if (tls_connect_socket(ctx, s, servername) != 0) { close(s); goto err; } ctx->socket = s; rv = 0; err: free(hs); free(ps); return (rv); } int tls_connect_socket(struct tls *ctx, int s, const char *servername) { return tls_connect_fds(ctx, s, s, servername); } int tls_connect_fds(struct tls *ctx, int fd_read, int fd_write, const char *servername) { union tls_addr addrbuf; int rv = -1; if ((ctx->flags & TLS_CLIENT) == 0) { tls_set_errorx(ctx, "not a client context"); goto err; } if (fd_read < 0 || fd_write < 0) { tls_set_errorx(ctx, "invalid file descriptors"); goto err; } if (servername != NULL) { if ((ctx->servername = strdup(servername)) == NULL) { tls_set_errorx(ctx, "out of memory"); goto err; } } if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) { tls_set_errorx(ctx, "ssl context failure"); goto err; } if (tls_configure_ssl(ctx) != 0) goto err; if (tls_configure_keypair(ctx, 0) != 0) goto err; if (ctx->config->verify_name) { if (servername == NULL) { tls_set_errorx(ctx, "server name not specified"); goto err; } } if (ctx->config->verify_cert && (tls_configure_ssl_verify(ctx, SSL_VERIFY_PEER) == -1)) goto err; if (SSL_CTX_set_tlsext_status_cb(ctx->ssl_ctx, tls_ocsp_verify_callback) != 1) { tls_set_errorx(ctx, "ssl OCSP verification setup failure"); goto err; } if ((ctx->ssl_conn = SSL_new(ctx->ssl_ctx)) == NULL) { tls_set_errorx(ctx, "ssl connection failure"); goto err; } if (SSL_set_app_data(ctx->ssl_conn, ctx) != 1) { tls_set_errorx(ctx, "ssl application data failure"); goto err; } if (SSL_set_rfd(ctx->ssl_conn, fd_read) != 1 || SSL_set_wfd(ctx->ssl_conn, fd_write) != 1) { tls_set_errorx(ctx, "ssl file descriptor failure"); goto err; } if (SSL_set_tlsext_status_type(ctx->ssl_conn, TLSEXT_STATUSTYPE_ocsp) != 1) { tls_set_errorx(ctx, "ssl OCSP extension setup failure"); goto err; } /* * RFC4366 (SNI): Literal IPv4 and IPv6 addresses are not * permitted in "HostName". */ if (servername != NULL && inet_pton(AF_INET, servername, &addrbuf) != 1 && inet_pton(AF_INET6, servername, &addrbuf) != 1) { if (SSL_set_tlsext_host_name(ctx->ssl_conn, servername) == 0) { tls_set_errorx(ctx, "server name indication failure"); goto err; } } rv = 0; err: return (rv); } int tls_handshake_client(struct tls *ctx) { X509 *cert = NULL; int ssl_ret; int rv = -1; if ((ctx->flags & TLS_CLIENT) == 0) { tls_set_errorx(ctx, "not a client context"); goto err; } ERR_clear_error(); if ((ssl_ret = SSL_connect(ctx->ssl_conn)) != 1) { rv = tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "handshake"); goto err; } if (ctx->config->verify_name) { cert = SSL_get_peer_certificate(ctx->ssl_conn); if (cert == NULL) { tls_set_errorx(ctx, "no server certificate"); goto err; } if ((rv = tls_check_name(ctx, cert, ctx->servername)) != 0) { if (rv != -2) tls_set_errorx(ctx, "name `%s' not present in" " server certificate", ctx->servername); goto err; } } ctx->state |= TLS_HANDSHAKE_COMPLETE; rv = 0; err: X509_free(cert); return (rv); } #endif /* USUAL_LIBSSL_FOR_TLS */ pgbouncer-1.7/lib/usual/tls/tls_verify.c0000664000175000017500000001427112615704017015333 00000000000000/* $OpenBSD: tls_verify.c,v 1.7 2015/02/11 06:46:33 jsing Exp $ */ /* * Copyright (c) 2014 Jeremie Courreges-Anglas * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "tls_compat.h" #ifdef USUAL_LIBSSL_FOR_TLS #include #include "tls_internal.h" static int tls_match_name(const char *cert_name, const char *name); static int tls_check_subject_altname(struct tls *ctx, X509 *cert, const char *name); static int tls_check_common_name(struct tls *ctx, X509 *cert, const char *name); static int tls_match_name(const char *cert_name, const char *name) { const char *cert_domain, *domain, *next_dot; if (strcasecmp(cert_name, name) == 0) return 0; /* Wildcard match? */ if (cert_name[0] == '*') { /* * Valid wildcards: * - "*.domain.tld" * - "*.sub.domain.tld" * - etc. * Reject "*.tld". * No attempt to prevent the use of eg. "*.co.uk". */ cert_domain = &cert_name[1]; /* Disallow "*" */ if (cert_domain[0] == '\0') return -1; /* Disallow "*foo" */ if (cert_domain[0] != '.') return -1; /* Disallow "*.." */ if (cert_domain[1] == '.') return -1; next_dot = strchr(&cert_domain[1], '.'); /* Disallow "*.bar" */ if (next_dot == NULL) return -1; /* Disallow "*.bar.." */ if (next_dot[1] == '.') return -1; domain = strchr(name, '.'); /* No wildcard match against a name with no host part. */ if (name[0] == '.') return -1; /* No wildcard match against a name with no domain part. */ if (domain == NULL || strlen(domain) == 1) return -1; if (strcasecmp(cert_domain, domain) == 0) return 0; } return -1; } /* See RFC 5280 section 4.2.1.6 for SubjectAltName details. */ static int tls_check_subject_altname(struct tls *ctx, X509 *cert, const char *name) { STACK_OF(GENERAL_NAME) *altname_stack = NULL; union tls_addr addrbuf; int addrlen, type; int count, i; int rv = -1; altname_stack = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); if (altname_stack == NULL) return -1; if (inet_pton(AF_INET, name, &addrbuf) == 1) { type = GEN_IPADD; addrlen = 4; } else if (inet_pton(AF_INET6, name, &addrbuf) == 1) { type = GEN_IPADD; addrlen = 16; } else { type = GEN_DNS; addrlen = 0; } count = sk_GENERAL_NAME_num(altname_stack); for (i = 0; i < count; i++) { GENERAL_NAME *altname; altname = sk_GENERAL_NAME_value(altname_stack, i); if (altname->type != type) continue; if (type == GEN_DNS) { void *data; int format, len; format = ASN1_STRING_type(altname->d.dNSName); if (format == V_ASN1_IA5STRING) { data = ASN1_STRING_data(altname->d.dNSName); len = ASN1_STRING_length(altname->d.dNSName); if (len < 0 || len != (int)strlen(data)) { tls_set_errorx(ctx, "error verifying name '%s': " "NUL byte in subjectAltName, " "probably a malicious certificate", name); rv = -2; break; } /* * Per RFC 5280 section 4.2.1.6: * " " is a legal domain name, but that * dNSName must be rejected. */ if (strcmp(data, " ") == 0) { tls_set_error(ctx, "error verifying name '%s': " "a dNSName of \" \" must not be " "used", name); rv = -2; break; } if (tls_match_name(data, name) == 0) { rv = 0; break; } } else { #ifdef DEBUG fprintf(stdout, "%s: unhandled subjectAltName " "dNSName encoding (%d)\n", getprogname(), format); #endif } } else if (type == GEN_IPADD) { unsigned char *data; int datalen; datalen = ASN1_STRING_length(altname->d.iPAddress); data = ASN1_STRING_data(altname->d.iPAddress); if (datalen < 0) { tls_set_errorx(ctx, "Unexpected negative length for an " "IP address: %d", datalen); rv = -2; break; } /* * Per RFC 5280 section 4.2.1.6: * IPv4 must use 4 octets and IPv6 must use 16 octets. */ if (datalen == addrlen && memcmp(data, &addrbuf, addrlen) == 0) { rv = 0; break; } } } sk_GENERAL_NAME_pop_free(altname_stack, GENERAL_NAME_free); return rv; } static int tls_check_common_name(struct tls *ctx, X509 *cert, const char *name) { X509_NAME *subject_name; char *common_name = NULL; union tls_addr addrbuf; int common_name_len; int rv = -1; subject_name = X509_get_subject_name(cert); if (subject_name == NULL) goto out; common_name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName, NULL, 0); if (common_name_len < 0) goto out; common_name = calloc(common_name_len + 1, 1); if (common_name == NULL) goto out; X509_NAME_get_text_by_NID(subject_name, NID_commonName, common_name, common_name_len + 1); /* NUL bytes in CN? */ if (common_name_len != (int)strlen(common_name)) { tls_set_errorx(ctx, "error verifying name '%s': " "NUL byte in Common Name field, " "probably a malicious certificate", name); rv = -2; goto out; } if (inet_pton(AF_INET, name, &addrbuf) == 1 || inet_pton(AF_INET6, name, &addrbuf) == 1) { /* * We don't want to attempt wildcard matching against IP * addresses, so perform a simple comparison here. */ if (strcmp(common_name, name) == 0) rv = 0; else rv = -1; goto out; } if (tls_match_name(common_name, name) == 0) rv = 0; out: free(common_name); return rv; } int tls_check_name(struct tls *ctx, X509 *cert, const char *name) { int rv; rv = tls_check_subject_altname(ctx, cert, name); if (rv == 0 || rv == -2) return rv; return tls_check_common_name(ctx, cert, name); } #endif /* USUAL_LIBSSL_FOR_TLS */ pgbouncer-1.7/lib/usual/tls/tls_config.c0000664000175000017500000001713612615704017015277 00000000000000/* $OpenBSD: tls_config.c,v 1.8 2015/02/22 14:59:37 jsing Exp $ */ /* * Copyright (c) 2014 Joel Sing * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "tls_compat.h" #ifdef USUAL_LIBSSL_FOR_TLS #include #include "tls_internal.h" static int set_string(const char **dest, const char *src) { free((char *)*dest); *dest = NULL; if (src != NULL) if ((*dest = strdup(src)) == NULL) return -1; return 0; } static void * memdup(const void *in, size_t len) { void *out; if ((out = malloc(len)) == NULL) return NULL; memcpy(out, in, len); return out; } static int set_mem(char **dest, size_t *destlen, const void *src, size_t srclen) { free(*dest); *dest = NULL; *destlen = 0; if (src != NULL) if ((*dest = memdup(src, srclen)) == NULL) return -1; *destlen = srclen; return 0; } struct tls_config * tls_config_new(void) { struct tls_config *config; if ((config = calloc(1, sizeof(*config))) == NULL) return (NULL); /* * Default configuration. */ if (tls_config_set_ca_file(config, _PATH_SSL_CA_FILE) != 0) goto err; if (tls_config_set_dheparams(config, "none") != 0) goto err; if (tls_config_set_ecdhecurve(config, "auto") != 0) goto err; if (tls_config_set_ciphers(config, "secure") != 0) goto err; tls_config_set_protocols(config, TLS_PROTOCOLS_DEFAULT); tls_config_set_verify_depth(config, 6); tls_config_prefer_ciphers_server(config); tls_config_verify(config); return (config); err: tls_config_free(config); return (NULL); } void tls_config_free(struct tls_config *config) { if (config == NULL) return; tls_config_clear_keys(config); free((char *)config->ca_file); free((char *)config->ca_path); free((char *)config->cert_file); free(config->cert_mem); free((char *)config->ciphers); free((char *)config->key_file); free(config->key_mem); free(config); } void tls_config_clear_keys(struct tls_config *config) { tls_config_set_ca_mem(config, NULL, 0); tls_config_set_cert_mem(config, NULL, 0); tls_config_set_key_mem(config, NULL, 0); } int tls_config_parse_protocols(uint32_t *protocols, const char *protostr) { uint32_t proto, protos = 0; char *s, *p, *q; int negate; if ((s = strdup(protostr)) == NULL) return (-1); q = s; while ((p = strsep(&q, ",:")) != NULL) { while (*p == ' ' || *p == '\t') p++; negate = 0; if (*p == '!') { negate = 1; p++; } if (negate && protos == 0) protos = TLS_PROTOCOLS_ALL; proto = 0; if (strcasecmp(p, "all") == 0 || strcasecmp(p, "legacy") == 0) proto = TLS_PROTOCOLS_ALL; else if (strcasecmp(p, "default") == 0 || strcasecmp(p, "secure") == 0) proto = TLS_PROTOCOLS_DEFAULT; if (strcasecmp(p, "tlsv1") == 0) proto = TLS_PROTOCOL_TLSv1; else if (strcasecmp(p, "tlsv1.0") == 0) proto = TLS_PROTOCOL_TLSv1_0; else if (strcasecmp(p, "tlsv1.1") == 0) proto = TLS_PROTOCOL_TLSv1_1; else if (strcasecmp(p, "tlsv1.2") == 0) proto = TLS_PROTOCOL_TLSv1_2; if (proto == 0) { free(s); return (-1); } if (negate) protos &= ~proto; else protos |= proto; } *protocols = protos; free(s); return (0); } int tls_config_set_ca_file(struct tls_config *config, const char *ca_file) { return set_string(&config->ca_file, ca_file); } int tls_config_set_ca_path(struct tls_config *config, const char *ca_path) { return set_string(&config->ca_path, ca_path); } int tls_config_set_ca_mem(struct tls_config *config, const uint8_t *ca, size_t len) { return set_mem(&config->ca_mem, &config->ca_len, ca, len); } int tls_config_set_cert_file(struct tls_config *config, const char *cert_file) { return set_string(&config->cert_file, cert_file); } int tls_config_set_cert_mem(struct tls_config *config, const uint8_t *cert, size_t len) { return set_mem(&config->cert_mem, &config->cert_len, cert, len); } int tls_config_set_ciphers(struct tls_config *config, const char *ciphers) { if (ciphers == NULL || strcasecmp(ciphers, "default") == 0 || strcasecmp(ciphers, "secure") == 0) ciphers = TLS_CIPHERS_DEFAULT; else if (strcasecmp(ciphers, "compat") == 0 || strcasecmp(ciphers, "legacy") == 0) ciphers = TLS_CIPHERS_COMPAT; else if (strcasecmp(ciphers, "insecure") == 0 || strcasecmp(ciphers, "all") == 0) ciphers = TLS_CIPHERS_ALL; else if (strcasecmp(ciphers, "normal") == 0) ciphers = TLS_CIPHERS_NORMAL; else if (strcasecmp(ciphers, "fast") == 0) ciphers = TLS_CIPHERS_FAST; return set_string(&config->ciphers, ciphers); } int tls_config_set_dheparams(struct tls_config *config, const char *params) { int keylen; if (params == NULL || strcasecmp(params, "none") == 0) keylen = 0; else if (strcasecmp(params, "auto") == 0) keylen = -1; else if (strcasecmp(params, "legacy") == 0) keylen = 1024; else return (-1); config->dheparams = keylen; return (0); } int tls_config_set_ecdhecurve(struct tls_config *config, const char *name) { int nid; if (name == NULL || strcasecmp(name, "none") == 0) nid = NID_undef; else if (strcasecmp(name, "auto") == 0) nid = -1; else if ((nid = OBJ_txt2nid(name)) == NID_undef) return (-1); config->ecdhecurve = nid; return (0); } int tls_config_set_key_file(struct tls_config *config, const char *key_file) { return set_string(&config->key_file, key_file); } int tls_config_set_key_mem(struct tls_config *config, const uint8_t *key, size_t len) { if (config->key_mem) explicit_bzero(config->key_mem, config->key_len); return set_mem(&config->key_mem, &config->key_len, key, len); } int tls_config_set_ocsp_stapling_file(struct tls_config *config, const char *blob_file) { if (blob_file != NULL) tls_config_set_ocsp_stapling_mem(config, NULL, 0); return set_string(&config->ocsp_file, blob_file); } int tls_config_set_ocsp_stapling_mem(struct tls_config *config, const uint8_t *blob, size_t len) { if (blob != NULL) tls_config_set_ocsp_stapling_file(config, NULL); return set_mem(&config->ocsp_mem, &config->ocsp_len, blob, len); } void tls_config_set_protocols(struct tls_config *config, uint32_t protocols) { config->protocols = protocols; } void tls_config_set_verify_depth(struct tls_config *config, int verify_depth) { config->verify_depth = verify_depth; } void tls_config_prefer_ciphers_client(struct tls_config *config) { config->ciphers_server = 0; } void tls_config_prefer_ciphers_server(struct tls_config *config) { config->ciphers_server = 1; } void tls_config_insecure_noverifycert(struct tls_config *config) { config->verify_cert = 0; } void tls_config_insecure_noverifyname(struct tls_config *config) { config->verify_name = 0; } void tls_config_insecure_noverifytime(struct tls_config *config) { config->verify_time = 0; } void tls_config_verify(struct tls_config *config) { config->verify_cert = 1; config->verify_name = 1; config->verify_time = 1; } void tls_config_verify_client(struct tls_config *config) { config->verify_client = 1; } void tls_config_verify_client_optional(struct tls_config *config) { config->verify_client = 2; } #endif /* USUAL_LIBSSL_FOR_TLS */ pgbouncer-1.7/lib/usual/strpool.h0000664000175000017500000000350312511203511014032 00000000000000/* * Pool for shared strings. * * Copyright (c) 2010 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Storage for shared strings. * * This provides refcounted searchable string pool for cases * where lot of objects reference same strings. */ #ifndef _USUAL_STRPOOL_H_ #define _USUAL_STRPOOL_H_ #include /** Handle for the pool */ struct StrPool; /** Pooled String */ struct PStr { /** Parent pool */ struct StrPool *pool; /** String length */ size_t len; /** Reference count */ int refcnt; /** Zero-terminated value */ char str[FLEX_ARRAY]; }; /** Create new pool */ struct StrPool *strpool_create(CxMem *ca); /** Release pool */ void strpool_free(struct StrPool *sp); /** Return either existing or new PStr for given value */ struct PStr *strpool_get(struct StrPool *sp, const char *str, ssize_t len); /** Increase reference count for existing PStr */ void strpool_incref(struct PStr *str); /** Decrease reference count for existing PStr */ void strpool_decref(struct PStr *str); /** Return count of strings in the pool */ int strpool_total(struct StrPool *sp); #endif pgbouncer-1.7/lib/usual/cxalloc.h0000664000175000017500000000765512557724300014007 00000000000000/* * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * Context-based Memory Allocator. * * The idea is that each data structure is given a context to allocate from, * and it can create subcontext for that which can be specific allocation * pattern that matches the data structure. * * It is slightly more work to use than palloc (PostgreSQL) or talloc (Samba), * but it avoids the need to have big fully-featured framework that does * everything at once. * * Instead you have small task-specific allocators, and you can always fall * back to raw malloc if you want to valgrind the code. * * Potential variants: * - fully-featured pooled * - randomly failing * - logging * - locking * - guard signatures * - palloc / talloc like API */ #ifndef _USUAL_CXALLOC_H_ #define _USUAL_CXALLOC_H_ #include /** * Ops for allocator that takes context. * * NB! - they are not equivalent to cx_* API. The cx_* * functions do additional sanitizing. */ struct CxOps { /** * Allocate memory. * len will not be 0. */ void *(*c_alloc)(void *ctx, size_t len); /** * Resize existing allocation. * Both p and len will not be 0 */ void *(*c_realloc)(void *ctx, void *p, size_t len); /** * Free existing allocation. * p will not be 0 */ void (*c_free)(void *ctx, const void *p); /** * Release all memory in context. * This is not supported by all allocators. */ void (*c_destroy)(void *ctx); }; /** * Memory allocation context. */ struct CxMem { const struct CxOps *ops; void *ctx; }; /** Shortcut to const CxMem */ typedef const struct CxMem CxMem; /* * Basic operations on allocation context. */ /** * Allocate memory from context. * * Returns NULL if no memory or len == 0. */ void *cx_alloc(CxMem *cx, size_t len) _MALLOC; /** * Change existing allocation. * * If ptr is NULL it creates new allocation. * If len is 0 it frees the memory. */ void *cx_realloc(CxMem *cx, void *ptr, size_t len); /** * Free existing allocation. * * Does nothing if ptr is NULL. */ void cx_free(CxMem *cx, const void *ptr); /** * Release all memory allocated in context. * * Should be called only on contexts that support it. */ void cx_destroy(CxMem *cx); /** Allocate and zero-fill memory */ void *cx_alloc0(CxMem *cx, size_t len) _MALLOC; /** Allocate and copy */ void *cx_memdup(CxMem *cx, const void *src, size_t len) _MALLOC; /** Allocate and copy string */ void *cx_strdup(CxMem *cx, const char *str) _MALLOC; /** Print to allocated string, return length or -1 on error. */ int cx_asprintf(CxMem *cx, char **dst_p, const char *fmt, ...) _PRINTF(3, 4); /** Print to allocated string, return length or -1 on error */ int cx_vasprintf(CxMem *cx, char **dst_p, const char *fmt, va_list ap) _PRINTF(3, 0); /** Print to allocated string, return new string or NULL on error */ char *cx_sprintf(CxMem *cx, const char *fmt, ...) _PRINTF(2, 3); /** Print to allocated string, return new string or NULL on error */ char *cx_vsprintf(CxMem *cx, const char *fmt, va_list ap) _PRINTF(2, 0); /** Allocator that uses libc malloc/realloc/free */ extern CxMem cx_libc_allocator; /** Default allocator */ #ifndef USUAL_ALLOC #define USUAL_ALLOC (&cx_libc_allocator) #endif #endif pgbouncer-1.7/lib/usual/base.h0000664000175000017500000002125012560223470013253 00000000000000/** @file * Basic C environment. */ /* * Copyright (c) 2007-2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _USUAL_BASE_H_ #define _USUAL_BASE_H_ #ifdef USUAL_TEST_CONFIG #include "test_config.h" #elif defined(_MSC_VER) #include #else #include #endif /* solaris is broken otherwise */ #if defined(__sun) #define _XPG4_2 #define __EXTENSIONS__ #endif /* C11 */ #ifndef __STDC_WANT_LIB_EXT1__ #define __STDC_WANT_LIB_EXT1__ 1 #endif #include #ifdef HAVE_SYS_PARAM_H #include #endif #include #include #ifdef HAVE_INTTYPES_H #include #endif #include #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_STDBOOL_H #include #else /* we really want bool type */ typedef enum { true=1, false=0 } bool; #endif #ifdef WIN32 #include #define DLLEXPORT __declspec(dllexport) #define DLLIMPORT __declspec(dllimport) #else #define DLLEXPORT #define DLLIMPORT #endif #ifndef PRIdZ #define PRIdZ "zd" /**< printf 'd' format specifier for ssize_t */ #define PRIiZ "zi" /**< printf 'i' format specifier for ssize_t */ #define PRIoZ "zo" /**< printf 'o' format specifier for size_t */ #define PRIuZ "zu" /**< printf 'u' format specifier for size_t */ #define PRIxZ "zx" /**< printf 'x' format specifier for size_t */ #define PRIXZ "zX" /**< printf 'X' format specifier for size_t */ #endif /** give offset of a field inside struct */ #ifndef offsetof #define offsetof(type, field) ((unsigned long)&(((type *)0)->field)) #endif /** given pointer to field inside struct, return pointer to struct */ #ifndef container_of #define container_of(ptr, type, field) ((type *)((char *)(ptr) - offsetof(type, field))) #endif /** get alignment requirement for a type */ #ifndef alignof #define alignof(type) offsetof(struct { char c; type t; }, t) #endif /** power-of-2 alignment */ #ifndef CUSTOM_ALIGN #define CUSTOM_ALIGN(x, a) (((uintptr_t)(x) + (uintptr_t)(a) - 1) & ~((uintptr_t)(a) - 1)) #endif /** preferred alignment */ #ifndef ALIGN #define ALIGN(x) CUSTOM_ALIGN(x, sizeof(long)) #endif /** number of elements in array */ #define ARRAY_NELEM(a) (sizeof(a) / sizeof((a)[0])) /** * Compat helper to specify array with unknown length. * * Usage: * * @code * char flex_string[FLEX_ARRAY]; * @endcode */ #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) #define FLEX_ARRAY #elif defined(__GNUC__) && (__GNUC__ >= 3) #define FLEX_ARRAY #else #define FLEX_ARRAY 1 #endif /** Make string token from C expression */ #define STR(x) _STR_(x) #define _STR_(x) #x /** Make single C token from 2 separate tokens */ #define CONCAT(a, b) _CONCAT_(a, b) #define _CONCAT_(a, b) a ## b /** Make single C token from 3 separate tokens */ #define CONCAT3(a, b, c) _CONCAT3_(a, b, c) #define _CONCAT3_(a, b, c) a ## b ## c /** Make single C token from 4 separate tokens */ #define CONCAT4(a, b, c, d) _CONCAT4_(a, b, c, d) #define _CONCAT4_(a, b, c, d) a ## b ## c ## d /** Pre-processor macro for current function name. */ #ifndef HAVE_FUNCNAME__FUNC #define __func__ __FUNCTION__ #endif /** * @name Compiler checks, mainly for internal usage. * * @{ */ /** Pre-processor macro to check if compiler is GCC with high enough version */ #define _COMPILER_GNUC(maj,min) (defined(__GNUC__) && \ ((__GNUC__ > (maj)) || (__GNUC__ == (maj) && __GNUC_MINOR__ >= (min)))) /** Pre-processor macro to check if compiler is CLANG with high enough version */ #define _COMPILER_CLANG(maj,min) (defined(__clang__) && \ ((__clang_major__ > (maj)) || (__clang_major__ == (maj) && __clang_minor__ >= (min)))) /** Pre-processor macro to check if compiler is Visual C with high enough version */ #define _COMPILER_MSC(ver) (defined(_MSC_VER) && (_MSC_VER >= (ver))) /** Pre-processor macro to check if compiler is Intel CC with high enough version */ #define _COMPILER_ICC(ver) (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= (ver))) /* * clang compat * * They work only if the compiler is clang, * return 0 otherwise. */ #ifndef __has_builtin #define __has_builtin(x) (0) #endif #ifndef __has_feature #define __has_feature(x) (0) #endif #ifndef __has_extension #define __has_extension(x) __has_feature(x) #endif #ifndef __has_attribute #define __has_attribute(x) (0) #endif /* * clang macros that cannot be defined here: * __is_identifier * __has_include * __has_include_next * __has_warning */ /** * @} * * @name Function/struct attributes. * * @{ */ /** Disable padding for structure */ #ifndef _MSC_VER #define _PACKED __attribute__((packed)) #endif /* * make compiler do something useful */ /** Show warning if function result is not used */ #if _COMPILER_GNUC(4,0) || __has_attribute(warn_unused_result) #define _MUSTCHECK __attribute__((warn_unused_result)) #else #define _MUSTCHECK #endif /** Show warning if used */ #if _COMPILER_GNUC(4,0) || __has_attribute(deprecated) #define _DEPRECATED __attribute__((deprecated)) #else #define _DEPRECATED #endif /** Check printf-style format and arg sanity */ #if _COMPILER_GNUC(4,0) || __has_attribute(printf) #define _PRINTF(fmtpos, argpos) __attribute__((format(printf, fmtpos, argpos))) #else #define _PRINTF(fmtpos, argpos) #endif /** Function returns new pointer */ #if _COMPILER_GNUC(4,0) || __has_attribute(malloc) #define _MALLOC __attribute__((malloc)) #else #define _MALLOC #endif /** Disable 'unused' warning for function/argument. */ #if _COMPILER_GNUC(4,0) || __has_attribute(unused) #define _UNUSED __attribute__((unused)) #else #define _UNUSED #endif /** Do not inline function. */ #if _COMPILER_GNUC(4,0) || __has_attribute(noinline) #define _NOINLINE __attribute__((noinline)) #else #define _NOINLINE #endif /** Indicates that function never returns */ #if _COMPILER_GNUC(4,0) || __has_attribute(noreturn) #define _NORETURN __attribute__((noreturn)) #else #define _NORETURN #endif /** Hint for compiler that expression (x) is likely to be true */ #if _COMPILER_GNUC(4,0) || __has_builtin(__builtin_expect) #define likely(x) __builtin_expect(!!(x), 1) #else #define likely(x) (x) #endif /** Hint for compiler that expression (x) is likely to be false */ #if _COMPILER_GNUC(4,0) || __has_builtin(__builtin_expect) #define unlikely(x) __builtin_expect(!!(x), 0) #else #define unlikely(x) (x) #endif /* @} */ /** * Compile-time assert. * * Expression must be evaluatable at compile time. * If false, stop compilation with message. * * It can be used in either global or function scope. */ #ifndef static_assert #if _COMPILER_GNUC(4,6) || _COMPILER_MSC(1600) || __has_feature(c_static_assert) /* Version for new compilers */ #define static_assert(expr, msg) _Static_assert(expr, msg) #else /* Version for old compilers */ #define static_assert(expr, msg) enum { CONCAT4(static_assert_failure_, __LINE__, _, __COUNTER__) = 1/(1 != (1 + (expr))) } #endif #endif /* !static_assert */ /** assert() that uses module */ #ifndef Assert #ifdef CASSERT void log_fatal(const char *file, int line, const char *func, bool show_perror, void *ctx, const char *s, ...) _PRINTF(6, 7); #define Assert(e) \ do { \ if (unlikely(!(e))) { \ log_fatal(__FILE__, __LINE__, __func__, false, NULL, \ "Assert(%s) failed", #e); \ abort(); \ } \ } while (0) #else #define Assert(e) #endif #endif /* Fix posix bug by accepting const pointer. */ static inline void _const_free(const void *p) { free((void *)p); } /** Compat: make free() accept const pointer */ #define free(x) _const_free(x) /** Zeroing malloc */ _MUSTCHECK static inline void *zmalloc(size_t len) { return calloc(1, len); } #ifndef HAVE_POSIX_MEMALIGN #define posix_memalign(a,b,c) usual_memalign(a,b,c) /** Compat: posix_memalign() */ int posix_memalign(void **ptr_p, size_t align, size_t len); #endif #ifndef HAVE_REALLOCARRAY #define reallocarray(a,b,c) usual_reallocarray(a,b,c) /** * Same as realloc(), but safely calculates total size. */ void *reallocarray(void *p, size_t count, size_t size); #endif #endif pgbouncer-1.7/lib/usual/base.c0000664000175000017500000000330312556647420013257 00000000000000/* * Basic C environment. * * Copyright (c) 2007-2009 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #if defined(HAVE_MALLOC_H) && defined(__darwin__) #include #endif /* define posix_memalign() only when possible to emulate */ #if !defined(HAVE_POSIX_MEMALIGN) \ && (defined(HAVE_MEMALIGN) || defined(HAVE_VALLOC)) int posix_memalign(void **ptr_p, size_t align, size_t len) { void *p; int ret, old_errno = errno; #ifdef HAVE_MEMALIGN p = memalign(align, len); #else /* !HAVE_MEMALIGN */ #ifdef HAVE_VALLOC /* assuming less than pagesize alignment */ p = valloc(len); #endif /* !VALLOC */ #endif /* !MEMALIGN */ *ptr_p = p; if (p) return 0; /* on error restore old errno */ ret = errno; errno = old_errno; return ret; } #endif #ifndef HAVE_REALLOCARRAY void *reallocarray(void *p, size_t count, size_t size) { size_t total; if (!safe_mul_size(&total, count, size)) { errno = ENOMEM; return NULL; } return realloc(p, total); } #endif pgbouncer-1.7/lib/usual/json.h0000664000175000017500000002324012556647420013325 00000000000000/* * Read and write JSON. * * Copyright (c) 2014 Marko Kreen * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file * Read and write JSON. * * Features: * - Robust - does not crash or assert() on invalid data. * - Fast memory allocation - it uses single pooled area * for all objects thus alloc/free are fast. * - Strict UTF8 validation. * - Full int64_t and double passthrough, except NaN and +-Infinity. * - Proper number I/O even in weird locales. * * Optional features for JSON config files, off by default: * - Allow C comments. * - Allow extra comma in dict/list. */ #ifndef _USUAL_JSON_ #define _USUAL_JSON_ #include #include /** * JSON value types * * Returned by json_value_type(). */ enum JsonValueType { JSON_NULL = 1, /**< Null value */ JSON_BOOL, /**< Boolean value */ JSON_INT, /**< Integer value */ JSON_FLOAT, /**< Float value */ JSON_STRING, /**< String value */ JSON_LIST, /**< JSON list */ JSON_DICT, /**< JSON "object", which is key->value map */ }; /** Options for JSON parser. */ enum JsonParseOptions { /** Default - do strict parsing. No comments, no extra comma. */ JSON_STRICT = 0, /** Allow comments, allow extra comma. Useful for JSON in config files. */ JSON_PARSE_RELAXED = 1, /** Do not validate UTF-8. The default behavior is to validate UTF-8. */ JSON_PARSE_IGNORE_ENCODING = 2, }; /** * @struct JsonValue * * Json value */ struct JsonValue; /** * @struct JsonContext * * Allocation context. */ struct JsonContext; /** Callback for dict iterator */ typedef bool (*json_dict_iter_callback_f)(void *arg, struct JsonValue *key, struct JsonValue *val); /** Callback for list iterator */ typedef bool (*json_list_iter_callback_f)(void *arg, struct JsonValue *elem); /** * @name Allocation context. * * @{ */ /** Create allocation context */ struct JsonContext *json_new_context(const void *cx_mem, size_t initial_mem); /** Create allocation context */ void json_free_context(struct JsonContext *ctx); /** Create allocation context */ const char *json_strerror(struct JsonContext *ctx); /** * @} * * @name Parse JSON * * @{ */ /** Parse JSON string */ struct JsonValue *json_parse(struct JsonContext *ctx, const char *src, size_t length); /** Set parsing options */ void json_set_options(struct JsonContext *ctx, unsigned int options); /** * @} * * @name Examine single value * * @{ */ /** Return type for value */ enum JsonValueType json_value_type(struct JsonValue *jv); /** * Return element size. * * For JSON strings, it's bytes in string, for list and * dict it returns number of elements. */ size_t json_value_size(struct JsonValue *jv); /** Return true if value is null */ static inline bool json_value_is_null(struct JsonValue *jv) { return json_value_type(jv) == JSON_NULL; } /** Return true if value is boolean */ static inline bool json_value_is_bool(struct JsonValue *jv) { return json_value_type(jv) == JSON_BOOL; } /** Return true if value is int */ static inline bool json_value_is_int(struct JsonValue *jv) { return json_value_type(jv) == JSON_INT; } /** Return true if value is float */ static inline bool json_value_is_float(struct JsonValue *jv) { return json_value_type(jv) == JSON_FLOAT; } /** Return true if value is string */ static inline bool json_value_is_string(struct JsonValue *jv) { return json_value_type(jv) == JSON_STRING; } /** Return true if value is list */ static inline bool json_value_is_list(struct JsonValue *jv) { return json_value_type(jv) == JSON_LIST; } /** Return true if value is dict */ static inline bool json_value_is_dict(struct JsonValue *jv) { return json_value_type(jv) == JSON_DICT; } /** Get bool value */ bool json_value_as_bool(struct JsonValue *jv, bool *dst_p); /** Get int value */ bool json_value_as_int(struct JsonValue *jv, int64_t *dst_p); /** Get double value */ bool json_value_as_float(struct JsonValue *jv, double *dst_p); /** Get string value */ bool json_value_as_string(struct JsonValue *jv, const char **dst_p, size_t *size_p); /** * @} * * @name Get values from dict * * @{ */ /** Get key value from dict */ bool json_dict_get_value(struct JsonValue *dict, const char *key, struct JsonValue **val_p); /** Return true if value is null or missing */ bool json_dict_is_null(struct JsonValue *jv, const char *key); /** Get boolean value from dict */ bool json_dict_get_bool(struct JsonValue *jv, const char *key, bool *dst_p); /** Get int value from dict */ bool json_dict_get_int(struct JsonValue *jv, const char *key, int64_t *dst_p); /** Get float value from dict */ bool json_dict_get_float(struct JsonValue *jv, const char *key, double *dst_p); /** Get string value from dict */ bool json_dict_get_string(struct JsonValue *jv, const char *key, const char **dst_p, size_t *len_p); /** Get sub-dict from dict */ bool json_dict_get_dict(struct JsonValue *jv, const char *key, struct JsonValue **dst_p); /** Get list from dict */ bool json_dict_get_list(struct JsonValue *jv, const char *key, struct JsonValue **dst_p); /* * Optional elements */ /** Get optional bool from dict */ bool json_dict_get_opt_bool(struct JsonValue *jv, const char *key, bool *dst_p); /** Get optional int from dict */ bool json_dict_get_opt_int(struct JsonValue *jv, const char *key, int64_t *dst_p); /** Get optional float from dict */ bool json_dict_get_opt_float(struct JsonValue *jv, const char *key, double *dst_p); /** Get optional string from dict */ bool json_dict_get_opt_string(struct JsonValue *jv, const char *key, const char **dst_p, size_t *len_p); /** Get optional list from dict */ bool json_dict_get_opt_list(struct JsonValue *jv, const char *key, struct JsonValue **dst_p); /** Get optional dict from dict */ bool json_dict_get_opt_dict(struct JsonValue *jv, const char *key, struct JsonValue **dst_p); /** * @} * * @name Get values from list. * * @{ */ /** Get value from list */ bool json_list_get_value(struct JsonValue *list, size_t index, struct JsonValue **val_p); /** Return true if value is null or missing */ bool json_list_is_null(struct JsonValue *list, size_t index); /** Get bool from list */ bool json_list_get_bool(struct JsonValue *list, size_t index, bool *val_p); /** Get int from list */ bool json_list_get_int(struct JsonValue *list, size_t index, int64_t *val_p); /** Get float from list */ bool json_list_get_float(struct JsonValue *list, size_t index, double *val_p); /** Get string from list */ bool json_list_get_string(struct JsonValue *list, size_t index, const char **val_p, size_t *len_p); /** Get list value from list */ bool json_list_get_list(struct JsonValue *list, size_t index, struct JsonValue **val_p); /** Get dict value from list */ bool json_list_get_dict(struct JsonValue *list, size_t index, struct JsonValue **val_p); /** * @} * * @name Iterate over elements in list/dict. * * @{ */ /** Walk over dict elements */ bool json_dict_iter(struct JsonValue *dict, json_dict_iter_callback_f cb_func, void *cb_arg); /** Walk over list elements */ bool json_list_iter(struct JsonValue *list, json_list_iter_callback_f cb_func, void *cb_arg); /** * @} * * @name Output JSON. * * @{ */ /** Render JSON object as string */ bool json_render(struct MBuf *dst, struct JsonValue *jv); /** * @} * * @name Create new values. * * @{ */ /** Create NULL value */ struct JsonValue *json_new_null(struct JsonContext *ctx); /** Create bool value */ struct JsonValue *json_new_bool(struct JsonContext *ctx, bool val); /** Create int value */ struct JsonValue *json_new_int(struct JsonContext *ctx, int64_t val); /** Create float value */ struct JsonValue *json_new_float(struct JsonContext *ctx, double val); /** Create string value */ struct JsonValue *json_new_string(struct JsonContext *ctx, const char *val); /** Create dict value */ struct JsonValue *json_new_dict(struct JsonContext *ctx); /** Create list value */ struct JsonValue *json_new_list(struct JsonContext *ctx); /** * @} * * @name Add values to containers. * * @{ */ /** Add value to list */ bool json_list_append(struct JsonValue *list, struct JsonValue *elem); /** Add null to list */ bool json_list_append_null(struct JsonValue *list); /** Add bool to list */ bool json_list_append_bool(struct JsonValue *list, bool val); /** Add int to list */ bool json_list_append_int(struct JsonValue *list, int64_t val); /** Add float to list */ bool json_list_append_float(struct JsonValue *list, double val); /** Add string to list */ bool json_list_append_string(struct JsonValue *list, const char *val); /** Add element to dict */ bool json_dict_put(struct JsonValue *dict, const char *key, struct JsonValue *val); /** Add null to dict */ bool json_dict_put_null(struct JsonValue *dict, const char *key); /** Add bool to dict */ bool json_dict_put_bool(struct JsonValue *dict, const char *key, bool val); /** Add int to dict */ bool json_dict_put_int(struct JsonValue *dict, const char *key, int64_t val); /** Add float to dict */ bool json_dict_put_float(struct JsonValue *dict, const char *key, double val); /** Add string to dict */ bool json_dict_put_string(struct JsonValue *dict, const char *key, const char *str); /** * @} */ #endif pgbouncer-1.7/lib/usual/getopt.h0000664000175000017500000000670012511202014013631 00000000000000/* * Copyright (c) 2000 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Dieter Baron and Thomas Klausner. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * @file * getopt compat. * * This module provides getopt() and getopt_long(). */ #ifndef _USUAL_GETOPT_H_ #define _USUAL_GETOPT_H_ #include #ifndef NEED_USUAL_GETOPT #if !defined(HAVE_GETOPT_H) || !defined(HAVE_GETOPT) || !defined(HAVE_GETOPT_LONG) #define NEED_USUAL_GETOPT #endif #endif #ifndef NEED_USUAL_GETOPT /* Use system getopt */ #include #else /* NEED_USUAL_GETOPT */ /* avoid name collision */ #define optarg usual_optarg #define opterr usual_opterr #define optind usual_optind #define optopt usual_optopt #define getopt(a,b,c) usual_getopt(a,b,c) #define getopt_long(a,b,c,d,e) usual_getopt_long(a,b,c,d,e) /** argument to current option, or NULL if it has none */ extern char *optarg; /** Current position in arg string. Starts from 1. Setting to 0 resets state. */ extern int optind; /** whether getopt() should print error messages on problems. Default: 1. */ extern int opterr; /** Option char which caused error */ extern int optopt; /** long option takes no argument */ #define no_argument 0 /** long option requires argument */ #define required_argument 1 /** long option has optional argument */ #define optional_argument 2 /** Long option description */ struct option { /** name of long option */ const char *name; /** * whether option takes an argument. * One of no_argument, required_argument, and optional_argument. */ int has_arg; /** if not NULL, set *flag to val when option found */ int *flag; /** if flag not NULL, value to set *flag to; else return value */ int val; }; /** Compat: getopt */ int getopt(int argc, char *argv[], const char *options); /** Compat: getopt_long */ int getopt_long(int argc, char *argv[], const char *options, const struct option *longopts, int *longindex); /** Compat: getopt_long_only */ int getopt_long_only(int nargc, char *argv[], const char *options, const struct option *long_options, int *idx); #endif /* NEED_USUAL_GETOPT */ #endif /* !_USUAL_GETOPT_H_ */ pgbouncer-1.7/lib/mk/0000775000175000017500000000000012635051616011532 500000000000000pgbouncer-1.7/lib/mk/amext-libusual.mk0000664000175000017500000000323512556647420014750 00000000000000# # Merge libusual sources with target sources # # Usage: # USUAL_DIR = # # _EMBED_LIBUSUAL = 1 # # It adds module sources into _SOURCES # and -I$(USUAL_DIR) to _CPPFLAGS. # ## ## Utility functions for libusual link ## _USUAL_DIR = $(call JoinPath,$(srcdir),$(USUAL_DIR)) _USUAL_DIR2 = $(call JoinPath,$(_USUAL_DIR),usual) # module names from sources (plus headers) UsualMods = $(trace1)$(shell $(_USUAL_DIR)/find_modules.sh $(_USUAL_DIR) $(wildcard $(addprefix $(srcdir)/,$(1)))) # full-path sources based on module list UsualSrcsFull = $(trace1)$(wildcard $(addprefix $(_USUAL_DIR2)/,$(addsuffix *.[ch],$(1)))) # remove USUAL_DIR UsualStrip = $(trace1)$(subst $(_USUAL_DIR)/,,$(1)) # simple-path sources based on module list UsualSrcs = $(call UsualStrip,$(call UsualSrcsFull,$(1))) # usual sources from user source file list UsualSources = $(if $(1),$(call UsualSrcsFull,$(call UsualMods,$(1)))) # 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags define EmbedLibUsual $(trace5) # embed libusual objects directly $(IFEQ) ($$($(1)_EMBED_LIBUSUAL),1) $(1)_SOURCES := $$($(1)_SOURCES) $$(call UsualSources, $$($(1)_SOURCES)) EXTRA_$(1)_SOURCES := $$(EXTRA_$(1)_SOURCES) \ $$(call UsualSources, \ $$(EXTRA_$(1)_SOURCES) \ $$(nodist_$(1)_SOURCES) \ $$(nodist_EXTRA_$(1)_SOURCES)) $(1)_CPPFLAGS += -I$$(USUAL_DIR) # add libusual to vpath $(IFEQ) ($$(filter $$(USUAL_DIR),$$(VPATH)),) VPATH += $$(USUAL_DIR) $(IFNEQ) ($$(srcdir),$$(builddir),) VPATH += $$(call JoinPath,$$(srcdir),$$(USUAL_DIR)) $(ENDIF) $(ENDIF) $(ENDIF) endef AM_TARGET_HOOKS += EmbedLibUsual EXTRA_DIST += $(_USUAL_DIR)/find_modules.sh $(_USUAL_DIR)/usual/config.h.in pgbouncer-1.7/lib/mk/amext-modes.mk0000664000175000017500000000446212511202014014213 00000000000000# # Custom compilation modes # Compile one target several times with different # configuration variables. # # Sample: # CFLAGS = -O2 # bin_PROGRAM = prog # prog_SOURCES = prog.c # # AM_MODES = debug # CFLAGS_debug = -O0 -g # # Result: # prog - compiled with -O2 # prog-debug - compiled with -O0 -g # AM_MODES ?= # Variables that can be overrided with $(var)_$(mode) AM_MODE_OVERRIDE += CC CXX CFLAGS CPPFLAGS DEFS LDFLAGS LIBS ## add "-MODE" string before file extension # 1-mode, 2-filename ModeName = $(basename $(2))-$(1)$(suffix $(2)) ## add mode suffix to all plain filenames # 1-mode, 2-file names, options ModeFilter = $(foreach f,$(2),$(if $(filter /% -%,$(f)),$(f),$(call ModeName,$(1),$(f)))) ## set per-target var # 1-dbgvar, 2-var, 3-final ModeVarX = $(3): $(2) = $$($(1))$(NewLine) # 1-mode, 2-var, 3-final ModeVarOverride = $(if $($(2)_$(1)),$(call ModeVarX,$(2)_$(1),$(2),$(3))) # 1-mode, 2-final ModeVarOverrideAll = $(foreach v,$(AM_MODE_OVERRIDE),$(call ModeVarOverride,$(1),$(v),$(2))) ## copy target, replace vars # 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags,6=mode,7-newtgt,8-cleantgt,9-list define AddModes4 $(trace8) $(IFEQ) ($$(filter $(9),$$(am_TARGETLISTS)),) am_TARGETLISTS += $(9) $(ENDIF) # add new target to old list $(9) += $(7) # copy details, change library names $(8)_SOURCES := $$($(1)_SOURCES) nodist_$$(8)_SOURCES := $$(nodist_$(1)_SOURCES) $(8)_CPPFLAGS := $$($(1)_CPPFLAGS) $(8)_CFLAGS := $$($(1)_CFLAGS) $(8)_LDFLAGS := $$($(1)_LDFLAGS) $(8)_LIBADD := $$(call ModeFilter,$(6),$$($(1)_LIBADD)) $(8)_LDADD := $$(call ModeFilter,$(6),$$($(1)_LDADD)) # add variable replacements $(call ModeVarOverrideAll,$(6),$(call FinalTargetFile,$(8),$(7),$(3))) endef ## add clean name, list name # 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags,6-mode,7-raw tgt AddModes3 = $(call AddModes4,$(1),$(2),$(3),$(4),$(5),$(6),$(7),$(call CleanName,$(7)),$(subst $(Space),_,$(5)_$(4)_$(3))) ## loop over modes # 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags AddModes2 = $(trace5)$(foreach m,$(AM_MODES),$(call AddModes3,$(1),$(2),$(3),$(4),$(5),$(m),$(call ModeName,$(m),$(2)))) ## ignore small primaries # 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags AddModes = $(trace5)$(if $(filter $(3),$(AM_BIG_PRIMARIES)),$(call AddModes2,$(1),$(2),$(3),$(4),$(5))) # Install hook AM_TARGET_HOOKS += AddModes pgbouncer-1.7/lib/mk/antimake.mk0000775000175000017500000012140712616105307013600 00000000000000#! /usr/bin/make -f # # antimake.mk - automake syntax with GNU Make # # Copyright (c) 2011 Marko Kreen # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # Goals: # - Clean user Makefiles, by using automake syntax # - Clean output during build # - Optional ties with `autoconf` and `libtool` # - Automatic dependency tracking # - Avoid separate build step for Makefiles # - No extra tools needed except GNU Make # Usage without autoconf: # - copy antimake.mk into source dir, then: include antimake.mk # - copy/link antimake.mk into PATH, then: include $(shell antimake.mk) # # Usage with autoconf: # - Copy to antimake.mk.in at top dir, then process with autoconf # to antimake.mk and include that one in Makefiles. # # - Have config.mak.in that also includes antimake.mk. # Suggestion: the separate file should include antimake.mk # using $(abs_top_srcdir) to support separate build dir. # # - Include config and antimake.mk separately in user Makefiles ## ## Startup hacks ## # detect GNU make version, confuse others $(eval GNUMAKE380=1) GNUMAKE381=$(or ,$(GNUMAKE380)) define GNUMAKE382 = $(GNUMAKE381) endef # give error of too old ifeq ($(GNUMAKE381),) $(error GNU Make 3.81+ required) endif # extra targets if this file is executed directly ifeq ($(words $(MAKEFILE_LIST)), 1) .PHONY: show-location show-config # default: print location. For "include $(shell antimake.mk)"-style usage. show-location: @echo $(MAKEFILE_LIST) # show autoconfigurable variables show-config: @grep '@[^ ]*@$$' $(MAKEFILE_LIST) endif ## ## Allow this file to be processed through autoconf ## # # to extract autoconfigurable values: # $ grep '@[^ ]*@$' antimake.mk > config.mk.in # $ antimake.mk show-config > config.mk.in # ifneq ($(filter-out @%,@PACKAGE_NAME@),) PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_VERSION = @PACKAGE_VERSION@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PORTNAME = @PORTNAME@ EXEEXT = @EXEEXT@ HAVE_CC_DEPFLAG = @HAVE_CC_DEPFLAG@ # C language CC = @CC@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CFLAGS = @CFLAGS@ DEFS = @DEFS@ WFLAGS = @WFLAGS@ # linking LD = @LD@ LDFLAGS = @LDFLAGS@ LIBS = @LIBS@ # static and shared libs AR = @AR@ ARFLAGS = @ARFLAGS@ RANLIB = @RANLIB@ LIBTOOL = @LIBTOOL@ # other tools SHELL = @SHELL@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_DATA = @INSTALL_DATA@ MKDIR_P = @MKDIR_P@ SED = @SED@ AWK = @AWK@ GREP = @GREP@ EGREP = @EGREP@ STRIP = @STRIP@ # install locations prefix = @prefix@ exec_prefix = @exec_prefix@ bindir = @bindir@ includedir = @includedir@ sbindir = @sbindir@ libexecdir = @libexecdir@ datarootdir = @datarootdir@ datadir = @datadir@ sysconfdir = @sysconfdir@ docdir = @docdir@ mandir = @mandir@ libdir = @libdir@ localedir = @localedir@ pkgdatadir = @pkgdatadir@ pkgconfigdir = @pkgconfigdir@ aclocaldir = @aclocaldir@ # autoconf values for top dir abs_top_srcdir ?= @abs_top_srcdir@ abs_top_builddir ?= @abs_top_builddir@ nosub_top_srcdir ?= @top_srcdir@ nosub_top_builddir ?= @top_builddir@ endif # end of @xx@ values ## ## In case of missing autoconf values, provide sane defaults ## PACKAGE_NAME ?= package PACKAGE_TARNAME ?= $(PACKAGE_NAME) PACKAGE_VERSION ?= 0.0 PACKAGE_STRING ?= $(PACKAGE_NAME) $(PACKAGE_VERSION) PACKAGE_URL ?= PACKAGE_BUGREPORT ?= PORTNAME ?= unix EXEEXT ?= HAVE_CC_DEPFLAG ?= yes # C language CC ?= cc CPP ?= cpp CPPFLAGS ?= CFLAGS ?= -O -g DEFS ?= # warning flags are keps separately to allow easy override WFLAGS ?= -Wall # add them to main flags now CFLAGS += $(WFLAGS) # linking LD ?= ld LDFLAGS ?= LIBS ?= # static and shared libs LIBTOOL ?= libtool AR ?= ar ARFLAGS ?= rcs ifeq ($(ARFLAGS),rv) ARFLAGS = rcs endif RANLIB ?= ranlib # other tools SHELL ?= /bin/sh INSTALL ?= install INSTALL_PROGRAM ?= $(INSTALL) INSTALL_SCRIPT ?= $(INSTALL) INSTALL_DATA ?= $(INSTALL) MKDIR_P ?= mkdir -p SED ?= sed AWK ?= awk GREP ?= grep EGREP ?= grep -E STRIP ?= strip # install locations prefix ?= /usr/local exec_prefix ?= ${prefix} bindir ?= ${exec_prefix}/bin includedir ?= ${prefix}/include sbindir ?= ${exec_prefix}/sbin libexecdir ?= ${exec_prefix}/libexec datarootdir ?= ${prefix}/share datadir ?= ${datarootdir} sysconfdir ?= ${prefix}/etc docdir ?= ${datarootdir}/doc/${PACKAGE_TARNAME} mandir ?= ${datarootdir}/man libdir ?= ${exec_prefix}/lib localedir ?= ${datarootdir}/locale pkgdatadir ?= ${datarootdir}/${PACKAGE_TARNAME} pkgconfigdir ?= ${libdir}/pkgconfig aclocaldir ?= ${datarootdir}/aclocal # autoconf values for top dir abs_top_srcdir ?= $(CURDIR) abs_top_builddir ?= $(CURDIR) # make sure nosub vals are not empty ifeq ($(nosub_top_builddir),) nosub_top_builddir = . endif ifeq ($(nosub_top_srcdir),) nosub_top_srcdir = . endif ## ## Variables for user makefiles ## # current subdirectory location from top dir (foo/bar) SUBLOC ?= . # subdirectories in current directory SUBDIRS ?= # extra files for clean targets CLEANFILES ?= DISTCLEANFILES ?= MAINTAINERCLEANFILES ?= # Additional flags for Makefile use, to avoid need # to touch flags coming from autoconf/cmdline AM_DEFS ?= AM_CPPFLAGS ?= AM_CFLAGS ?= AM_LDFLAGS ?= AM_LIBTOOLFLAGS ?= AM_MAKEFLAGS ?= AM_LIBS ?= # libusual sources, for embedded usage USUAL_DIR ?= . # V=1 -> verbose build V ?= 0 # turn on function tracing AM_TRACE ?= # default formats for 'dist' AM_DIST_DEFAULT ?= gzip ## ## Non-user-serviceable area ## # Hacking: # # - Uppercase names are simple (late) variables, lowercase names - targets, # mixedcase - functions that need to be $(call)-ed. # # - Minimal amount of shell should be used here. # # - Minimal amount of := and $(eval) # # - It's useful to indent the expressions for easier understanding. # Later the indendation needs to be removed, as whitespace is significant for Make. # Several functions must not add any extra whitespace. # # GNU Make features in new versions: # # 3.80 - 2002-10-03: base version. $(eval) $(value) $(MAKEFILE_LIST) $(.VARIABLES) $(call fixes) # 3.81 - 2006-04-01: $(or), $(and), $(lastword), $(abspath), $(realpath), $(info), $(flavor) # 3.82 - 2010-07-28: private, undefine, define var := # # This file should use only features from 3.80 ## ## command helpers ## CCLD ?= $(CC) COMPILE ?= $(CC) $(AM_DEFS) $(DEFS) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LINK ?= $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_AR ?= $(AR) $(ARFLAGS) LIBTOOLCMD ?= $(LIBTOOL) $(LIBTOOLQ) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) RM = rm -f ## ## Internals ## # varables that can be set per-target with target_VAR # they appear as AM_foo. [Not supported: COMPILE] AM_TARGET_VARIABLES += CFLAGS CPPFLAGS LDFLAGS LIBTOOLFLAGS DEFS LIBS # list of language (rather compiler) names AM_LANGUAGES += C AM_BIG_PRIMARIES += LIBRARIES LTLIBRARIES PROGRAMS AM_SMALL_PRIMARIES += HEADERS SCRIPTS DATA MANS # list of destinations per primary AM_DESTINATIONS += bin lib libexec sbin \ data doc include locale man sysconf \ pkgdata pkgconfig aclocal \ noinst EXTRA # primaries where 'dist' is default AM_DIST_PRIMARIES += HEADERS AM_PRIMARIES = $(AM_BIG_PRIMARIES) $(AM_SMALL_PRIMARIES) # distclean does rm -rf on that OBJDIR = .objs # extension for objects OBJEXT = .o # extension for static libraries LIBEXT = .a # files that need to be converted to objects AM_SRCEXTS = $(foreach lang,$(AM_LANGUAGES),$(AM_LANG_$(lang)_SRCEXTS)) # target types - big/small: with/without objects # list of flags, 'noinst' is taken as dest, 'base' is always default AM_FLAGS = base nobase dist nodist ## configure non-defult target params AM_PROGRAMS_InstFunc = ProgInstall AM_LTLIBRARIES_InstFunc = LTLibInstall AM_LTLIBRARIES_OBJEXT = .lo AM_SCRIPTS_InstFunc = ScriptInstall AM_MANS_InstFunc = ManInstall # files to distribute am_DISTFILES := am_FINAL_DISTFILES = $(sort $(am_DISTFILES)) AM_DIST_BASE = $(PACKAGE_TARNAME)-$(PACKAGE_VERSION) AM_ALL_TARGETS = ## ## Make dependencies work ## HAVE_CC_DEPFLAG ?= yes ifeq ($(HAVE_CC_DEPFLAG),yes) OBJDEPS = -MD -MP -MT $@ -MF $@.d endif ## ## Quiet by default, 'make V=1' shows commands ## # 1-dir MkDir = $(MKDIR_P) $(1) # 1-fmt, 2-args Printf = printf $(1) $(2) CTX ?= ifeq ($(V), 0) E = @$(call Printf,"%-4s %-8s %s\n","$(CTX)") Q = @ LIBTOOLQ = --silent MAKEFLAGS += --no-print-directory else E = @true Q = LIBTOOLQ = --silent endif ## ## libtool activation ## # libtool activates when detects %.lo / %.la pattern LTCOMPILE = $(if $(filter %.lo,$@),$(LIBTOOLCMD) --mode=compile) LTLINK = $(if $(filter %.la %.lo,$^),$(LIBTOOLCMD) --mode=link) LTCLEAN = $(LIBTOOLCMD) --mode=clean ## ## Default setup for C ## AM_LANG_C_SRCEXTS = .c define AM_LANG_C_COMPILE $(E) "CC" $< $(Q) $(LTCOMPILE) $(COMPILE) $(OBJDEPS) -c -o $@ $< endef define AM_LANG_C_LINK $(E) "CCLD" $@ $(Q) $(LTLINK) $(LINK) $^ $(AM_LIBS) $(LIBS) $(AM_LT_RPATH) endef ## ## Various other shortcuts ## define ar_lib $(E) "AR" $@ $(Q) $(AM_AR) $@ $^ $(E) "RANLIB" $@ $(Q) $(RANLIB) $@ endef # 1 - dir define ProgInstall $(E) "INSTALL" "$< $(1)" $(Q) $(call MkDir,$(1)) $(Q) $(INSTALL_PROGRAM) $< $(1) endef # 1 - dir define ScriptInstall $(E) "INSTALL" "$< $(1)" $(Q) $(call MkDir,$(1)) $(Q) $(INSTALL_SCRIPT) $< $(1) endef # 1 - dir define DataInstall $(E) "INSTALL" "$< $(1)" $(Q) $(call MkDir,$(1)) $(Q) $(INSTALL_DATA) $< $(1) endef # 1 - dir, add manX subdir ManInstall = $(call DataInstall,$(1)/man$(call LastWord,$(subst ., ,$<))) # 1 - dir define LTLibInstall $(E) "INSTALL" "$< $(1)" $(Q) $(call MkDir,$(1)) $(Q) $(LIBTOOLCMD) --mode=install $(INSTALL) $< $(1) endef ## ## Create .srcext -> .obj mapping for a language ## # 1-tgt, 2-name, 3-srcext define LangObjTarget $(trace3) $$(OBJDIR)/$(1)/%$(OBJEXT) $$(OBJDIR)/$(1)/%.lo: %$(3) @$$(call MkDir,$$(dir $$@)) $$(AM_LANG_$(2)_COMPILE) endef # 1=tgt, 2=name define LangSetup $(trace2) $(foreach ext,$(AM_LANG_$(2)_SRCEXTS),$(call LangObjTarget,$(1),$(2),$(ext))$(NewLine)) endef ## ## Utility functions ## # for function debugging, put them at the start of body ifdef AM_TRACE trace1=$(warning $0('$1')) trace2=$(warning $0('$1','$2')) trace3=$(warning $0('$1','$2','$3')) trace4=$(warning $0('$1','$2','$3','$4')) trace5=$(warning $0('$1','$2','$3','$4','$5')) trace6=$(warning $0('$1','$2','$3','$4','$5','$6')) trace7=$(warning $0('$1','$2','$3','$4','$5','$6','$7')) trace8=$(warning $0('$1','$2','$3','$4','$5','$6','$7','$8')) trace9=$(warning $0('$1','$2','$3','$4','$5','$6','$7','$8','$9')) endif # for use inside $(eval) IFDEF = ifdef IFEQ = ifeq IFNEQ = ifneq ELSE = else ENDIF = endif # returns 'true' if $1==$2 Eq = $(if $(1)$(2),$(if $(findstring $(1),$(2)),$(if $(findstring $(2),$(1)),true)),true) Not = $(if $(1),,true) Neq = $(call Not,$(call Eq,$(1),$(2))) # replace [-./] with '_' CleanName = $(subst /,_,$(subst -,_,$(subst .,_,$(1)))) # return last word from word list LastWord = $(if $(1),$(word $(words $(1)),$(1))) Empty = Space = $(Empty) $(Empty) # twice to unconfuse syntax hiliters SQuote = ' SQuote = ' define NewLine endef # quote str for shell ShellQuote = '$(subst $(SQuote),'\$(SQuote)',$(1))' # replace extensions # 1-src ext list # 2-target ext # 3-source list ReplaceExts = $(foreach ext,$(1),$(patsubst %$(ext),%$(2),$(filter %$(ext),$(3)))) # objs with objdir from source list (1-cleantgt, 2-src list) SourceObjs = $(trace1)$(call SourceObjsExt,$(1),$(OBJEXT),$(2)) # objs with objdir from source list # 1-cleantgt, 2-objext, 3-srcs list SourceObjsExt = $(addprefix $(call JoinPath,$(OBJDIR),$(1))/, $(call ReplaceExts,$(AM_SRCEXTS),$(2),$(3))) # dependency files from object files, must match OBJDEPS DepFiles = $(wildcard $(addsuffix .d,$(1))) # per-target var override, 1=target, 2=varname # if foo_VAR exists, expand to: # build_foo install_foo clean_foo: AM_VAR = $(foo_VAR) # 1-tgt, 2-var, 3-final TgtVar2 = $(3): AM_$(2) = $$($(1)_$(2))$(NewLine) TgtVar = $(if $($(1)_$(2)),$(call TgtVar2,$(1),$(2),$(3))) # loop TgtVar over AM_TARGET_VARIABLES, 1=target, 2-final VarOverride = $(foreach var,$(AM_TARGET_VARIABLES),$(call TgtVar,$(1),$(var),$(2))) # check if actual target (.h, .exe) is nodist based on primary and flags # 1-prim 2-flags TargetNoDist = $(strip $(if $(filter nodist,$(2)), \ true, \ $(if $(filter dist,$(2)), \ , \ $(filter-out $(AM_DIST_PRIMARIES),$(1))))) # return sources that match language # 1-lang # 2-sources LangFiles = $(filter $(addprefix %,$(AM_LANG_$(1)_SRCEXTS)),$(2)) # return list of langs that match sources. # 1-sources LangList = $(strip $(foreach lang,$(AM_LANGUAGES),$(if $(call LangFiles,$(lang),$(1)),$(lang)))) # 1-sources LinkLangList = $(foreach lang,$(call LangList,$(1)),$(if $(AM_LANG_$(lang)_LINK),$(lang))) # pick linker variable based on sources, fallback to C # 1-sources DetectLinkVar = AM_LANG_$(call LastWord,C $(call LinkLangList,$(1)))_LINK # convert 'foo/bar' -> '../..' UpDirStep1 = $(subst /, ,$(1)) UpDirStep2 = $(foreach dir,$(call UpDirStep1,$(1)),../) UpDirStep3 = $(subst / ,/,$(call UpDirStep2,$(1))) UpDirStep4 = $(patsubst %/,%,$(call UpDirStep3,$(1))) UpDir = $(if $(filter-out .,$(1)),$(call UpDirStep4,$(1)),.) # # AntiMake requires that joining clean names must result in clean names. # # Thus: # JoinPath(.,foo) -> foo # JoinPath(foo,/abs) -> /abs # JoinPath(a/b,../c) -> a/c # JoinPath(a,../../b/c) -> ../b/c # # 1-path, 2-last name : foo => . | /foo => / | foo/bar => foo CutLastName = $(if $(filter $(2),$(1)),.,$(if $(filter /$(2),$(1)),/,$(patsubst %/$(2),%,$(1)))) # 1-path component, remove last elem : CutLast = $(call CutLastName,$(1),$(lastword $(subst /, ,$(1)))) # 1/2 : actual place where / is put JoinPathFinal = $(if $(filter /,$(1)),$(1)$(2),$(1)/$(2)) # 1/2 : second starts with ../, remove it and last component of $(1) JoinPath5 = $(call JoinPath,$(call CutLast,$(1)),$(patsubst ../%,%,$(2))) # 1/2: check if first ends with .. JoinPath4 = $(if $(filter .. %/..,$(1)),$(call JoinPathFinal,$(1),$(2)),$(call JoinPath5,$(1),$(2))) # 1/2 : check if second starts with ..; otherwise join JoinPath3 = $(if $(filter ../%,$(2)),$(call JoinPath4,$(1),$(2)),$(call JoinPathFinal,$(1),$(2))) # 1/2 : skips component if '.' JoinPath2 = $(if $(filter-out .,$(1)),$(if $(filter-out .,$(2)),$(call JoinPath3,$(1),$(2)),$(1)),$(2)) # 1/2 : check if b is absolute, otherwise fix minor problems JoinPath = $(trace2)$(if $(filter /%,$(2)),$(2),$(call JoinPath2,$(if $(filter /,$(1)),$(1),$(patsubst %/,%,$(1))),$(patsubst ./%,%,$(2)))) ## ## Parse target list variables ## ## pick out components from name, call function # 1-varname, 2-words, 3-func, 4-func arg # func args: 1-var, 2-prim, 3-dest, 4-flags, 5-arg ParseName = $(call $(3),$(1),$(filter $(AM_PRIMARIES),$(2)),$(filter $(AM_DESTINATIONS),$(2)),$(filter $(AM_FLAGS),$(2)),$(4)) ForEachList = $(foreach var,$(2),$(call ParseName,$(var),$(subst _, ,$(var)),$(1),$(3))) ## try reconstruct name, if fails, its a random variable # 1-var, 2-prim,3-dest,4-flags CheckName = $(if $(call Eq,$(subst _, ,$(1)),$(strip $(4) $(call LastWord,$(3)) $(call LastWord,$(2)))),$(1)) ## also check if variable is filled # 1-var, 2-prim,3-dest,4-flags CheckNameFull = $(if $(call CheckName,$(1),$(2),$(3),$(4)),$(if $($(1)),$(1))) ## ## Loop over targets in list variables ## ## call function on parsed target # 1-var, 2-prim, 3-dest, 4-flags, 5-func # func args: 1-cleantgt, 2-tgt, 3-prim, 4-dest, 5-flags ForEachTarget2 = $(foreach tgt,$($(1)),$(call $(5),$(call CleanName,$(tgt)),$(tgt),$(2),$(3),$(4))) ## ForEachTarget: call function on all targets in lists # 1-func, 2- var list # func args: 1-cleantgt, 2-tgt, 3-prim, 4-dest, 5-flags ForEachTarget = $(call ForEachList,ForEachTarget2,$(2),$(1)) ## EMBED_SUBDIRS relocations ## add subdir to files # 1-subdir, 2-file list RelocFiles = $(foreach f,$(2),$(if $(filter -%,$(f)),$(f),$(call JoinPath,$(1),$(f)))) # 1-dir, 2-pfx, 3-full RelocOneFlag2 = $(2)$(call JoinPath,$(1),$(patsubst $(2)%,%,$(3))) # 1-dir, 2-flag RelocOneFlag = $(if $(filter -L%,$(2)), \ $(call RelocOneFlag2,$(1),-L,$(2)), \ $(if $(filter -I%,$(2)), \ $(call RelocOneFlag2,$(1),-I,$(2)), \ $(2))) ## Relocate relative files, relative -I/-L, ignore -* # 1-dir, 2- flaglist RelocFlags = $(strip $(if $(filter-out .,$(1)), \ $(foreach flg,$(2),$(call RelocOneFlag,$(1),$(flg))), \ $(2))) ## Separate build dir relocation ## non-local source dir: -Isrc/include -> -Isrc/include -I$(srcdir)/src/include # 1-srcdir, 2-flag list FixIncludes = $(strip $(if $(filter-out .,$(1)), \ $(foreach flg,$(2),$(call FixIncludes2,$(1),$(flg))), \ $(2))) # 1-dir, 2-flg FixIncludes2 = $(if $(filter -I%,$(2)), \ $(call FixIncludes3,$(1),$(patsubst -I%,%,$(2))), \ $(2)) # 1-dir, 2-orig dir FixIncludes3 = -I$(2) -I$(call JoinPath,$(srcdir),$(2)) ## ## Makefile fragments ## ### fill values # abs_top_srcdir, abs_top_builddir # nosub_top_builddir, nosub_top_srcdir # 1 - subdir define SetDirs abs_builddir := $$(call JoinPath,$$(abs_top_builddir),$(1)) abs_srcdir := $$(call JoinPath,$$(abs_top_srcdir),$(1)) top_builddir := $$(call UpDir,$(1)) top_srcdir := $$(call JoinPath,$$(top_builddir),$$(nosub_top_srcdir)) builddir := . $(IFEQ) ($$(nosub_top_srcdir),$$(nosub_top_builddir)) srcdir := . $(ELSE) srcdir := $$(call JoinPath,$$(top_srcdir),$(1)) $(ENDIF) endef ## ## Embedded subdirs ## # func args: 1-cleantgt, 2-tgt, 3-prim, 4-dest, 5-flags define RelocBigTarget $(trace5) # move vars: $(foreach var,$(AM_TARGET_VARIABLES),$(NewLine)$$(am_PFX)_$(1)_$(var) := $$($(1)_$(var))) # move and relocate EXTRA_$$(am_PFX)_$(1)_SOURCES := $$(call RelocFiles,$$(am_DIR),$$(EXTRA_$(1)_SOURCES)) $$(am_PFX)_$(1)_SOURCES := $$(call RelocFiles,$$(am_DIR),$$($(1)_SOURCES)) $$(am_PFX)_$(1)_DEPENDENCIES := $$(call RelocFiles,$$(am_DIR),$$($(1)_DEPENDENCIES)) $$(am_PFX)_$(1)_LDADD := $$(call RelocFiles,$$(am_DIR),$$($(1)_LDADD)) $$(am_PFX)_$(1)_LIBADD := $$(call RelocFiles,$$(am_DIR),$$($(1)_LIBADD)) $$(am_PFX)_$(1)_CFLAGS := $$(call RelocFlags,$$(am_DIR),$$($(1)_CFLAGS)) $$(am_PFX)_$(1)_CPPFLAGS := $$(call RelocFlags,$$(am_DIR),$$($(1)_CPPFLAGS)) $$(am_PFX)_$(1)_LDFLAGS := $$(call RelocFlags,$$(am_DIR),$$($(1)_LDFLAGS)) # clean vars $(1)_SOURCES = $(1)_LDADD = $(1)_LIBADD = $(foreach var,$(AM_TARGET_VARIABLES),$(NewLine)$(1)_$(var) = ) endef ## pick actual func # func args: 1-cleantgt, 2-tgt, 3-prim, 4-dest, 5-flags define RelocTarget $(trace5) $(if $(filter $(AM_BIG_PRIMARIES),$(3)),$(call RelocBigTarget,$(1),$(2),$(3),$(4),$(5))) endef ## relocate target list # func args: 1-var, 2-prim, 3-dest, 4-flags, 5-arg define RelocTList $(trace5) # detect top and subdir target conflict - it's easier to detect # and error out than to work around the rare case $(IFNEQ) (,$$(filter $(2),$$(AM_BIG_PRIMARIES))) $(IFEQ) (.,$$(am_DIR)) am_TOP_NAMES += $$(foreach tgt,$$($(1)),$$(call CleanName,$$(tgt))) $(ELSE) $(IFNEQ) (,$$(filter $$(am_TOP_NAMES),$$(foreach tgt,$$($(1)),$$(call CleanName,$$(tgt))))) $$(error $$(NewLine)$$(NewLine)\ *** Target names used in top Makefile cannot be re-used in embedded Makefiles. $$(NewLine)\ *** The target variables (eg. _SOURCES) conflict is not handled yet) $(ENDIF) $(ENDIF) $(ENDIF) # move value under real_% $(IFEQ) ($(real_$(1)),) real_$(1) := $(ENDIF) real_$(1) += $$(call RelocFiles,$$(am_DIR),$$($(1))) $(1) = # remember in proper list $(IFEQ) ($(3),EXTRA) am_EXTRA_TARGETLISTS += real_$(1) $(ELSE) am_TARGETLISTS += real_$(1) $(ENDIF) endef ## process included values # 1-dir, 2-pfx, 3-tlist define EmbedProcess $(trace3) $(IFNEQ) ($$(filter $(1),$$(am_EMBED_DONE)),) $$(error Double entry in EMBED_SUBDIRS: $(1)) $(ENDIF) # init local vars am_DIR := $(1) am_LOC := $$(call JoinPath,$$(SUBLOC),$(1)) am_PFX := $(2) am_EMBED_DONE += $(1) # reloc & save vars am_DISTFILES += $$(call RelocFiles,$$(am_DIR),$$(EXTRA_DIST)) am_CLEANFILES += $$(call RelocFiles,$$(am_DIR),$$(CLEANFILES)) am_DISTCLEANFILES += $$(call RelocFiles,$$(am_DIR),$$(DISTCLEANFILES)) am_MAINTAINERCLEANFILES += $$(call RelocFiles,$$(am_DIR),$$(MAINTAINERCLEANFILES)) am_EMBED_TODO += $$(call RelocFiles,$$(am_DIR),$$(EMBED_SUBDIRS)) am_SUBDIRS += $$(call RelocFiles,$$(am_DIR),$$(SUBDIRS)) am_DIST_SUBDIRS += $$(call RelocFiles,$$(am_DIR),$$(DIST_SUBDIRS)) # clean vars for new dir EXTRA_DIST = CLEANFILES = DISTCLEANFILES = MAINTAINERCLEANFILES = EMBED_SUBDIRS = SUBDIRS = DIST_SUBDIRS = $(call SetDirs,$(call JoinPath,$(SUBLOC),$(1))) $(call ForEachTarget,RelocTarget,$(3)) $(call ForEachList,RelocTList,$(3)) endef ## read Makefile.am, process it # 1 - dir DoEmbed = $(trace1)$(strip \ $(if $(wildcard $(am_srcdir)/$(1)/Makefile.am), \ $(eval include $(am_srcdir)/$(1)/Makefile.am $(NewLine)) \ $(eval $(call EmbedProcess,$(1),$(call CleanName,$(1)),$(AM_NONEXTRA_TLISTS) $(AM_EXTRA_TLISTS))), \ $(error $(SUBLOC)/Makefile failure: $(call JoinPath,$(SUBLOC),$(1)/Makefile.am) not found.))) ## ## Fragments that build targets ## # Note that variable initialization order is important here # as some of them will be used immediately. ## ## Install target object ## # 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags define InstallTarget $(trace5) $(1)_DEST := $$(if $$($(4)dir),$$($(4)dir),$$(error '$(4)dir' empty))$(if $(filter nobase,$(5)),/$(dir $(2))) $(1)_InstFunc := $$(if $$(AM_$(3)_InstFunc),$$(AM_$(3)_InstFunc),DataInstall) # actual installation .PHONY: install_$(1) install: install_$(1) install_$(1): $(2) $$(call $$($(1)_InstFunc),$$(DESTDIR)$$($(1)_DEST)) # hack to pass -rpath to LTLIBRARIES on build time (1) $(2): AM_DEST = $$($(1)_DEST) endef # hack to pass -rpath to LTLIBRARIES on build time (2) %.la: AM_LT_RPATH = $(if $(AM_DEST),-rpath $(AM_DEST)) ## ## Rules for big target ## # 1-varname, 2-ifset, 3-ifnotset IfSet = $(if $(filter-out undefined,$(flavor $(1))),$(2),$(3)) # 1-clean, 2-raw, 3-prim PROGRAMS_Final = $(if $($(1)_EXT),$(2)$($(1)_EXT),$(2)$(EXEEXT)) # 1-clean, 2-raw, 3-prim LIBRARIES_Final = $(if $($(1)_EXT),$(2)$($(1)_EXT),$(patsubst %.a,%$(LIBEXT),$(2))) # calculate target file name # 1-clean, 2-raw, 3-prim FinalTargetFile = $(call IfSet,$(3)_Final,$(call $(3)_Final,$(1),$(2),$(3)),$(2)$($(1)_EXT)) # 1-objs FixObjs = $(patsubst %.a,%$(LIBEXT),$(1)) # 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags define BigTargetBuild $(trace5) AM_ALL_TARGETS += $(1) $(1)_ALLSRCS := $$($(1)_SOURCES) $$(EXTRA_$(1)_SOURCES) $$(nodist_$(1)_SOURCES) $$(nodist_EXTRA_$(1)_SOURCES) # calculate OBJS from SOURCES $(1)_OBJEXT := $$(if $$(AM_$(3)_OBJEXT),$$(AM_$(3)_OBJEXT),$$(OBJEXT)) $(1)_OBJS := $$(call SourceObjsExt,$(1),$$($(1)_OBJEXT), \ $$($(1)_SOURCES) $$(nodist_$(1)_SOURCES)) $(1)_OBJS_CLEAN := $$($(1)_OBJS) # include additional objects, move flags to _LIBS $(IFEQ) ($(3),PROGRAMS) $(1)_OBJS += $$(filter-out -%,$$($(1)_LDADD)) $(1)_LIBS += $$(filter -%,$$($(1)_LDADD)) $(ELSE) $(1)_OBJS += $$(filter-out -%,$$($(1)_LIBADD)) $(1)_LIBS += $$(filter -%,$$($(1)_LIBADD)) $(ENDIF) # autodetect linker, unless given $(IFEQ) ($($(1)_LINK),) $(1)_LINKVAR := $$(call DetectLinkVar,$$($(1)_ALLSRCS)) $(ELSE) $(1)_LINKVAR := $(1)_LINK $(ENDIF) # calculate target file name $(1)_FINAL = $(call FinalTargetFile,$(1),$(2),$(3)) # hook libtool into LTLIBRARIES cleanup $(IFEQ) ($(3),LTLIBRARIES) $(1)_RM = $$(LTCLEAN) $$(RM) $(ELSE) $(1)_RM = $$(RM) $(ENDIF) # fix includes in case of separate build dir $(1)_CPPFLAGS := $$(call FixIncludes,$$(srcdir),$$($(1)_CPPFLAGS)) $(1)_CFLAGS := $$(call FixIncludes,$$(srcdir),$$($(1)_CFLAGS)) # load dependencies -include .dummy. $$(call DepFiles, $$($(1)_OBJS)) # actual build, clean & install targets .PHONY: build_$(1) clean_$(1) # allow target-specific variables $$(eval $$(call VarOverride,$(1),$(call FinalTargetFile,$(1),$(2),$(3)))) # build and clean by default, unless flagged EXTRA $(IFNEQ) ($(4),EXTRA) all: build_$(1) $(ENDIF) clean: clean_$(1) # _DEPENDENCIES and nodist_SOURCES must exist before build starts. $$(call FixObjs,$$($(1)_OBJS)): $$($(1)_DEPENDENCIES) $$(nodist_$(1)_SOURCES) build_$(1): $$($(1)_FINAL) $$($(1)_FINAL): $$(call FixObjs,$$($(1)_OBJS)) @$$(call MkDir,$$(dir $$@)) $$($(if $(filter LIBRARIES,$(3)),ar_lib,$$($(1)_LINKVAR))) clean_$(1): $$(E) "CLEAN" "$$($(1)_FINAL)" $$(Q) $$($(1)_RM) -- $$($(1)_OBJS_CLEAN) $(if $(call TargetNoDist,$(3),$(5)),$$($(1)_FINAL)) DISTCLEANFILES += $$(nodist_$(1)_SOURCES) $$(nodist_EXTRA_$(1)_SOURCES) $(foreach lang,$(AM_LANGUAGES),$(call LangSetup,$(1),$(lang))) endef # 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags define BigTargetDist am_DISTFILES += $$(filter-out $$(nodist_EXTRA_$(1)_SOURCES) $$(nodist_$(1)_SOURCES),$$($(1)_SOURCES) \ $$(EXTRA_$(1)_SOURCES)) $(if $(call TargetNoDist,$(3),$(5)),,$$($(1)_FINAL)) endef # 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags define MakeBigTarget $(trace5) # build if first time $(IFEQ) ($(filter $(1),$(AM_ALL_TARGETS)),) $(call BigTargetBuild,$(1),$(2),$(3),$(4),$(5)) $(call BigTargetDist,$(1),$(2),$(3),$(4),$(5)) $(ELSE) # allow only EXTRA be double $(IFNEQ) ($(4),EXTRA) $$(error Target '$2' described listed several times) $(ENDIF) $(ENDIF) # call InstallTarget, for dest != (EXTRA, noinst) $(IFEQ) ($(filter EXTRA noinst,$(4)),) $(call InstallTarget,$(1),$$($(1)_FINAL),$(3),$(4),$(5)) $(ENDIF) endef ## ## Rules for small target ## # 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags define MakeSmallTarget $(trace5) AM_ALL_TARGETS += $(1) # should the target file be distributed or cleaned? $(IFEQ) ($(call TargetNoDist,$(3),$(5)),) am_DISTFILES += $(2) $(ELSE) CLEANFILES += $(2) $(ENDIF) # build if not EXTRA $(IFNEQ) ($(4),EXTRA) all: $(2) # install if not EXTRA or noinst $(IFNEQ) ($(4),noinst) $(call InstallTarget,$(1),$(2),$(3),$(4),$(5)) $(ENDIF) $(ENDIF) endef ## ## Fill GNU-style vars for subdir ## # preferred to top_srcdir/top_builddir topdir = $(top_builddir) ifneq ($(nosub_top_builddir),.) $(error Non-local builddir not supported) endif # initial locaton vars $(eval $(call SetDirs,$(SUBLOC))) ifneq ($(nosub_top_srcdir),$(nosub_top_builddir)) # use VPATH to find non-local sources VPATH += $(srcdir) # fix includes AM_CPPFLAGS := $(call FixIncludes,$(srcdir),$(AM_CPPFLAGS)) AM_CFLAGS := $(call FixIncludes,$(srcdir),$(AM_CFLAGS)) endif ## ## O= ## if given, create wrapper makefiles in target dir ## that include makefiles from source dir, then run ## make from target dir. ## ifneq ($(O),) # 1-makefile define WrapMakeFileCmd @$(call MkDir,$(dir $(O)/$(1))) @$(call Printf,'%s\n%s\n%s\n%s\n%s\n', \ 'abs_top_srcdir = $(CURDIR)' \ 'abs_top_builddir = $(call JoinPath,$(CURDIR),$(O))' \ 'nosub_top_srcdir = $(call UpDir,$(O))' \ 'nosub_top_builddir = .' \ 'include $(abs_top_srcdir)/$(1)') \ > $(O)/$(1) endef # 1-makefile WrapMakeFile = $(if $(wildcard $(O)/$(1)),,$(call WrapMakeFileCmd,$(1))$(NewLine)) # redirect whatever rule was given .PHONY: all $(MAKECMDGOALS) all $(filter-out all,$(MAKECMDGOALS)): $(if $(wildcard $(O)),,$(error O=$(O): Directory '$(O)' does not exist)) $(foreach mk,$(filter-out /%,$(MAKEFILE_LIST)),$(call WrapMakeFile,$(mk))) $(Q) $(MAKE) O= -C $(O) $(MAKECMDGOALS) # O=empty, this is main makefile else ## ## main targets, tie them with subdir and local targets ## # disable random rules .SUFFIXES: all: sub-all all-local clean: sub-clean clean-local install: sub-install install-local distclean: sub-distclean distclean-local maintainer-clean: sub-maintainer-clean maintainer-clean-local .PHONY: all clean install dist distclean maintainer-clean # -local are empty targets by default .PHONY: all-local clean-local install-local distclean-local maintainer-clean-local all-local clean-local install-local distclean-local maintainer-clean-local: ## ## Actual embedding starts ## AM_ALL_TLISTS2 = $(filter $(addprefix %,$(AM_PRIMARIES)),$(.VARIABLES)) AM_ALL_TLISTS = $(call ForEachList,CheckName,$(AM_ALL_TLISTS2)) AM_NONEXTRA_TLISTS = $(filter-out EXTRA_%,$(AM_ALL_TLISTS)) AM_EXTRA_TLISTS = $(filter EXTRA_%,$(AM_ALL_TLISTS)) am_srcdir := $(srcdir) am_DIR := . am_PFX := am_TARGETLISTS := am_EXTRA_TARGETLISTS := am_TOP_NAMES := # move top-level targets away $(eval $(call ForEachList,RelocTList,$(AM_NONEXTRA_TLISTS))) $(eval $(call ForEachList,RelocTList,$(AM_EXTRA_TLISTS))) am_SUBDIRS := $(SUBDIRS) am_DIST_SUBDIRS := $(DIST_SUBDIRS) am_DISTFILES := $(EXTRA_DIST) am_CLEANFILES := $(CLEANFILES) am_DISTCLEANFILES := $(DISTCLEANFILES) am_MAINTAINERCLEANFILES := $(MAINTAINERCLEANFILES) am_EMBED_NOW := $(EMBED_SUBDIRS) am_EMBED_DONE := am_EMBED_TODO := EXTRA_DIST = CLEANFILES = DISTCLEANFILES = MAINTAINERCLEANFILES = SUBDIRS = DIST_SUBDIRS = EMBED_SUBDIRS = $(foreach dir,$(am_EMBED_NOW),$(call DoEmbed,$(dir))) am_EMBED_NOW := $(am_EMBED_TODO) am_EMBED_TODO := $(foreach dir,$(am_EMBED_NOW),$(call DoEmbed,$(dir))) am_EMBED_NOW := $(am_EMBED_TODO) am_EMBED_TODO := $(foreach dir,$(am_EMBED_NOW),$(call DoEmbed,$(dir))) am_EMBED_NOW := $(am_EMBED_TODO) am_EMBED_TODO := $(if $(am_EMBED_NOW),$(error EMBED_SUBDIRS recursion limit reached...)) # embedding done, move variables back $(eval $(call SetDirs,$(SUBLOC))) CLEANFILES := $(am_CLEANFILES) DISTCLEANFILES := $(am_DISTCLEANFILES) MAINTAINERCLEANFILES := $(am_MAINTAINERCLEANFILES) SUBDIRS := $(am_SUBDIRS) DIST_SUBDIRS := $(am_DIST_SUBDIRS) EMBED_SUBDIRS := $(am_EMBED_DONE) am_CLEANFILES = am_DISTCLEANFILES = am_MAINTAINERCLEANFILES = am_DIST_SUBDIRS = am_SUBDIRS = am_EMBED_DONE = am_TARGETLISTS := $(sort $(am_TARGETLISTS)) am_EXTRA_TARGETLISTS := $(sort $(am_EXTRA_TARGETLISTS)) # avoid duplicate entries with am_TARGETLISTS am_EXTRA_TARGETLISTS := $(filter-out $(am_TARGETLISTS),$(am_EXTRA_TARGETLISTS)) # allow seeing moved lists AM_FLAGS += real ## EMBED_SUBDIRS end ## ## Launch target hooks ## amdir = $(dir $(realpath $(filter %/antimake.mk antimake.mk,$(MAKEFILE_LIST)))) # 1-feat name FeatFile = $(call JoinPath,$(amdir),amext-$(1).mk) # 1- fname LoadFeature = $(if $(wildcard $(call FeatFile,$(1))),$(eval include $(call FeatFile,$(1))),$(error Feature "$(call FeatFile,$(1))" is not available.)) $(foreach f,$(AM_FEATURES),$(call LoadFeature,$(f))) $(eval $(foreach hook,$(AM_TARGET_HOOKS),$(call ForEachTarget,$(hook),$(am_TARGETLISTS)))) $(eval $(foreach hook,$(AM_TARGET_HOOKS),$(call ForEachTarget,$(hook),$(am_EXTRA_TARGETLISTS)))) ## ## Now generate the rules ## ## check which target func to call # 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags MakeTarget = $(call $(if $(filter $(AM_BIG_PRIMARIES),$(3)),MakeBigTarget,MakeSmallTarget),$(1),$(2),$(3),$(4),$(5)) ## process all targets in one list # 1-list, 2-prim,3-dest,4-flags MakeTargetList = $(foreach tgt,$($(1)),$(call MakeTarget,$(call CleanName,$(tgt)),$(tgt),$(2),$(3),$(4))) ## process all target lists # 1=list names ProcessTargets = $(call ForEachTarget,MakeTarget,$(1)) # process non-EXTRA targets $(eval $(call ProcessTargets,$(am_TARGETLISTS))) # process EXTRA_* last, they may already have been processed $(eval $(call ProcessTargets,$(am_EXTRA_TARGETLISTS))) ## ## clean targets ## clean: ifdef CLEANFILES $(E) "CLEAN" $@ $(Q) $(RM) -- $(CLEANFILES) endif distclean: clean $(E) "DISTCLEAN" $@ $(Q) $(RM) -r -- $(OBJDIR) ifdef DISTCLEANFILES $(Q) $(RM) -- $(DISTCLEANFILES) endif maintainer-clean: clean $(E) "MAINTAINERCLEAN" $@ $(Q) $(RM) -r -- $(OBJDIR) ifdef DISTCLEANFILES $(Q) $(RM) -- $(DISTCLEANFILES) endif ifdef MAINTAINERCLEANFILES $(Q) $(RM) -- $(MAINTAINERCLEANFILES) endif ## ## actual subdir targets ## # 1-dir define MakeSubDir $(trace1) $(E) "MKDIR" "Create $(call JoinPath,$(SUBLOC),$(1))" $(Q) $(call MkDir,$(1)) $(Q) $(call Printf,"include $(call UpDir,$(1))/$(srcdir)/$(1)/Makefile\n") \ > $(1)/Makefile endef # 1-dir, 2-tgt define SubTarget $(trace2) $(if $(wildcard $(1)/Makefile),,$(call MakeSubDir,$(1))) $(E) "-->" "$(call JoinPath,$(SUBLOC),$(1))" $(Q) $(MAKE) -C $(1) $(2) $(E) "<--" "$(call JoinPath,$(SUBLOC),$(1))" endef sub-all sub-install sub-clean: $(foreach dir,$(SUBDIRS),$(call SubTarget,$(dir),$(subst sub-,,$@))$(NewLine)) # Avoid double dirs in DIST_SUBDIRS, without changing order am_DISTDIRS = $(SUBDIRS) $(foreach dir,$(DIST_SUBDIRS),$(if $(filter $(dir),$(SUBDIRS)),,$(dir))) sub-dist sub-distclean sub-maintainer-clean: $(foreach dir,$(am_DISTDIRS),$(call SubTarget,$(dir),$(subst sub-,,$@))$(NewLine)) .PHONY: sub-all sub-clean sub-install sub-dist sub-distclean sub-maintainer-clean ## ## actual dist targets ## DistTarget = $(foreach fmt,$(1),dist-$(fmt)) AM_DIST_ALL ?= gzip bzip2 xz zip AM_DIST_ALL_TGTS = $(call DistTarget,$(AM_DIST_ALL)) AM_DIST_DEF_TGTS = $(call DistTarget,$(AM_DIST_DEFAULT)) AM_FORMAT_gzip_EXT = tar.gz AM_FORMAT_gzip_CMD = tar chof - $(AM_DIST_BASE) | gzip > $(AM_DIST_BASE).$(AM_FORMAT_gzip_EXT) AM_FORMAT_bzip2_EXT = tar.bz2 AM_FORMAT_bzip2_CMD = tar chof - $(AM_DIST_BASE) | bzip2 > $(AM_DIST_BASE).$(AM_FORMAT_bzip2_EXT) AM_FORMAT_xz_EXT = tar.xz AM_FORMAT_xz_CMD = tar chof - $(AM_DIST_BASE) | xz > $(AM_DIST_BASE).$(AM_FORMAT_xz_EXT) AM_FORMAT_zip_EXT = zip AM_FORMAT_zip_CMD = zip -rq $(AM_DIST_BASE).$(AM_FORMAT_zip_EXT) $(AM_DIST_BASE) # 1-name define MakeDist $(E) "CHECK" $@ $(Q) $(MAKE) -s am-check-distfiles $(E) "MKDIR" $(AM_DIST_BASE) $(Q) $(RM) -r -- $(AM_DIST_BASE) $(AM_DIST_BASE).$(AM_DIST_$(1)_EXT) $(Q) $(call MkDir,$(AM_DIST_BASE)) $(E) "COPY" $(AM_DIST_BASE) $(Q) $(MAKE) -s am-show-distfiles | cpio -pmduL --quiet $(AM_DIST_BASE) $(E) "PACK" $(AM_DIST_BASE).$(AM_FORMAT_$(1)_EXT) $(Q) $(AM_FORMAT_$(1)_CMD) $(Q) $(RM) -r -- $(AM_DIST_BASE) endef .PHONY: dist $(AM_DIST_ALL_TGTS) dist: $(AM_DIST_DEF_TGTS) dist-all: $(AM_DIST_ALL_TGTS) $(AM_DIST_ALL_TGTS): $(call MakeDist,$(subst dist-,,$@)) # show list of files that need to be in final archive .PHONY: am-show-distfiles am-show-distfiles: $(foreach dir,$(am_DISTDIRS),@$(MAKE) $(AM_MAKEFLAGS) --no-print-directory -C $(dir) $@ $(NewLine)) $(foreach file,$(am_FINAL_DISTFILES),@$(call Printf,"$(call JoinPath,$(SUBLOC),$(file))\n") $(NewLine)) # do dependencies as separate step, in case building outputs anything .PHONY: am-check-distfiles am-check-distfiles: $(am_FINAL_DISTFILES) $(foreach dir,$(am_DISTDIRS),@$(MAKE) $(AM_MAKEFLAGS) -C $(dir) $@ $(NewLine)) ## ## debug target ## # 1=var define AmDebugShow $(if $($(1)),@$(call Printf,"$(1) = $($(1))\n")) $(NewLine) endef # 1=cleantgt,2=rawtgt,3=prim,4=dest,5=flags define AmDebugTarget $(trace5) $(foreach var,$(AM_DEBUG_TARGET_VARS),$(call AmDebugShow,$(1)_$(var))) @$(call Printf,"\n") endef # func args: 1-var, 2-prim, 3-dest, 4-flags CollectDests = $(filter-out noinst EXTRA,$(3)) AM_USED_DESTS = $(sort $(call ForEachList,CollectDests,$(am_TARGETLISTS))) AM_DEBUG_VARS = GNUMAKE380 GNUMAKE381 GNUMAKE382 MAKEFILE_LIST \ AM_LANGUAGES AM_FLAGS AM_DESTINATIONS \ AM_ALL_TARGETS EXEEXT am_FINAL_DISTFILES \ nosub_top_builddir nosub_top_srcdir \ abs_top_srcdir abs_top_builddir \ srcdir builddir top_srcdir top_builddir \ SUBDIRS EMBED_SUBDIRS DIST_SUBDIRS \ DISTFILES CLEANFILES DISTCLEANFILES MAINTAINERCLEANFILES AM_DEBUG_TARGET_VARS = SOURCES OBJS LINKVAR DEST USUAL_OBJS USUAL_SRCS EXT FINAL \ $(AM_TARGET_VARIABLES) AM_DEBUG_LANG_VARS = SRCEXTS am-debug: @$(call Printf,"\n==== Global Variables ====\n") $(foreach var,$(AM_DEBUG_VARS),$(call AmDebugShow,$(var))) @$(call Printf,"\n==== Per-language Variables ====\n") $(foreach lg,$(AM_LANGUAGES),$(foreach var,$(AM_DEBUG_LANG_VARS),$(call AmDebugShow,AM_LANG_$(lg)_$(var)))) @$(call Printf,"\n==== Per-target Variables ====\n") $(call ForEachTarget,AmDebugTarget,$(am_TARGETLISTS) $(am_EXTRA_TARGETLISTS)) @$(call Printf,"\n==== Active install directories ====\n") $(foreach dst,$(AM_USED_DESTS),@$(call Printf," $(dst)dir = $($(dst)dir)\n" $(NewLine))) ## ## regtests for basic tools ## AM_TESTS = 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 AM_TEST_1 = $(call Eq,a b c,a b c),$(call Eq,,),$(call Eq,a,aa),$(call Eq,a,a a) AM_TEST_1_RES = true,true,, AM_TEST_2 = $(call Neq,a,aa),$(call Neq,a,a) AM_TEST_2_RES = true, AM_TEST_3 = $(call CleanName,obj/foo-baz.x) AM_TEST_3_RES = obj_foo_baz_x AM_TEST_4 = $(call LastWord,a),$(call LastWord,a b c),$(call LastWord,) AM_TEST_4_RES = a,c, AM_TEST_5 = $(call ReplaceExts,.c .cpp X.foo,.o,s1.c s2.cpp s3X.foo s4.h) AM_TEST_5_RES = s1.o s2.o s3.o AM_TEST_5 = $(call LangList,foo.c c.foo),$(call LangList,foo.c c.foo f.cpp) AM_TEST_5_RES = C,C CXX AM_TEST_6 = $(call DetectLinkVar,foo.c c.foo),$(call DetectLinkVar,foo.c c.foo x.cpp),$(call DetectLinkVar,foo),$(call DetectLinkVar,) AM_TEST_6_RES = AM_LANG_C_LINK,AM_LANG_CXX_LINK,AM_LANG_C_LINK,AM_LANG_C_LINK AM_TEST_7 = $(call UpDir,foo)|$(call UpDir,)|$(call UpDir,.)|$(call UpDir,foo/bar)|$(call UpDir,a/b/c)| AM_TEST_7_RES = ..|.|.|../..|../../..| AM_TEST_8 = $(call JoinPath,.,.)|$(call JoinPath,,)|$(call JoinPath,a,.)|$(call JoinPath,.,b)|$(call JoinPath,a,b)|$(call JoinPath,a/b,../c)|$(call JoinPath,a/b,../../../c) AM_TEST_8_RES = .||a|b|a/b|a/c|../c define AM_TEST_9_EVAL $(IFEQ) ($$(AM_TEST_9_RES),OK) AM_TEST_9 = OK $(ELSE) AM_TEST_9 = fail $(ENDIF) endef AM_TEST_9_RES = OK $(eval $(AM_TEST_9_EVAL)) AM_TEST_10 = $(call CheckName,nobase_bin_PROGRAMS,PROGRAMS,bin,nobase)|$(call CheckName,a,a,,)|$(call CheckName,bin_bin_DATA,,bin bin,DATA) AM_TEST_10_RES = nobase_bin_PROGRAMS|a| AM_TEST_11_Show = $(4)-$(3)-$(2) AM_TEST_11 = $(call ForEachList,AM_TEST_11_Show,bin_PROGRAMS foo_DATA baz_foo base_nobase_dist_nodist_DATA_PROGRAMS) AM_TEST_11_RES = -bin-PROGRAMS --DATA -- base nobase dist nodist--DATA PROGRAMS AM_TEST_12 = $(call RelocFlags,sub/dir,-I. -I./foo -Lfoo/bar -I/inc -L/lib -lfoo) AM_TEST_12_RES = -Isub/dir -Isub/dir/foo -Lsub/dir/foo/bar -I/inc -L/lib -lfoo AM_TEST_13 = $(call TargetNoDist,HEADERS,)|$(call TargetNoDist,HEADERS,nodist)|$(call TargetNoDist,PROGRAMS,)|$(call TargetNoDist,PROGRAMS,dist) AM_TEST_13_RES = |true|PROGRAMS| AM_TEST_14 = $(call ShellQuote,foo'bar\')|$(call ShellQuote,as!d' \\ $$foo) AM_TEST_14_RES = 'foo'\''bar\'\'''|'as!d'\'' \\ $$foo' AM_TEST_15 = $(call JoinPath,sub/dir,../foo) , \ $(call JoinPath,sub/dir,../../foo) , \ $(call JoinPath,sub/dir,../../../foo) , \ $(call JoinPath,sub/dir/,../foo) , \ $(call JoinPath,/,./foo) , \ $(call JoinPath,..,../foo) , \ $(call JoinPath,/foo,../baz) , \ $(call JoinPath,/foo,../../baz) , \ $(call JoinPath,foo/..,./foo) AM_TEST_15_RES = sub/foo , foo , ../foo , sub/foo , /foo , ../../foo , /baz , /baz , foo/../foo AM_TEST_16_EXT = .foo AM_TEST_16 = $(call FinalTargetFile,prog,prog,PROGRAMS) | $(call FinalTargetFile,AM_TEST_16,AM_TEST_16,PROGRAMS) AM_TEST_16_RES = prog$(EXEEXT) | AM_TEST_16.foo AmTest = $(if $(call Eq,$($(1)),$($(2))),@$(call Printf,"$(1): OK\n"),@$(call Printf,"$(subst ",',$(1): FAIL: $($(1)) != $($(2))\n)"))$(NewLine) am-test: $(Q) test "$(call Eq,a b c,a b c),$(call Eq,,),$(call Eq,a,aa),$(call Eq,a,a a)" = "true,true,," $(foreach nr,$(AM_TESTS),$(call AmTest,AM_TEST_$(nr),AM_TEST_$(nr)_RES)) ## ## help target ## AmHelpNames = targets standalone internal config dests .PHONY: help $(foreach n,$(AmHelpNames),help-$(n) help-$(n)-local) $(foreach n,$(AmHelpNames),help-$(n)-local): help: $(foreach n,$(AmHelpNames),help-$(n) help-$(n)-local) # 1-var, 2-desc AmConf = @$(call Printf," %-27s %s=%s\n" $(call ShellQuote,$(2)) $(call ShellQuote,$(1)) $(call ShellQuote,$($(1)))) help-targets: @$(call Printf,"\n") @$(call Printf,"Main targets:\n") @$(call Printf," all Build all targets (default)\n") @$(call Printf," install Install files\n") @$(call Printf," dist Create source archive\n") @$(call Printf," clean Clean built files\n") @$(call Printf," distclean Clean configured files\n") @$(call Printf," maintainer-clean Delete anything that can be generated\n") help-standalone: @$(call Printf,"\n") @$(call Printf,"Standalone targets: (make -f antimake.mk)\n") @$(call Printf," show-location Prints full path to antimake.mk (default)\n") @$(call Printf," show-config Prints template config.mak.in\n") help-internal: @$(call Printf,"\n") @$(call Printf,"Internal targets:\n") @$(call Printf," am-show-distfiles Shows files that go into source archive\n") @$(call Printf," am-debug Shows variables that affect the build\n") @$(call Printf," am-test Regtest for internal functions\n") help-config: @$(call Printf,"\n") @$(call Printf,"Config variables and their current values:\n") $(call AmConf,CC,C compiler) $(call AmConf,CFLAGS,C compiler flags) $(call AmConf,CPPFLAGS,C pre-processor flags) $(call AmConf,LDFLAGS,Linker flags) help-dests: @$(call Printf,"\n") @$(call Printf,"Destinations for install [ prefix=$(prefix) ]:\n") $(foreach dst,$(AM_USED_DESTS),@$(call Printf," $(dst)dir = $($(dst)dir)\n") $(NewLine)) endif # O=empty pgbouncer-1.7/lib/mk/std-autogen.sh0000775000175000017500000000451012567562476014262 00000000000000#! /bin/sh # autogen for non-automake trees # # - it installs files: config.sub, config.guess, install-sh # - it installs ltmain.sh, if LT_INIT or *LIBTOOL macro is used # set -e USUAL_DIR="$1" test -n "${USUAL_DIR}" || USUAL_DIR="." test -f "${USUAL_DIR}/m4/usual.m4" || { echo usage: $0 USUAL_DIR exit 1 } # default programs ACLOCAL=${ACLOCAL:-aclocal} AUTOCONF=${AUTOCONF:-autoconf} AUTOHEADER=${AUTOHEADER:-autoheader} # If neither ACLOCAL/AUTOCONF/AUTOHEADER and # AUTOCONF_VERSION/AUTOMAKE_VERSION are configured, # pick any modern version to avoid pointless errors. if test "$AUTOCONF_VERSION" = ""; then if test "$AUTOCONF" = "autoconf"; then for ac in 70 69 68 67 66 65 64 63 62 61 60 59; do ac="2.$ac" if which autoconf-$ac > /dev/null 2>&1; then AUTOCONF_VERSION="$ac" echo "Using autoconf: $AUTOCONF_VERSION" break fi if which autoconf$ac > /dev/null 2>&1; then AUTOCONF_VERSION="$ac" echo "Using autoconf: $AUTOCONF_VERSION" break fi done fi fi if test "$AUTOMAKE_VERSION" = ""; then if test "$ACLOCAL" = "aclocal"; then for am in 1.16 1.15 1.14 1.13 1.12 1.11 1.10 1.9; do if which aclocal-$am > /dev/null 2>&1; then AUTOMAKE_VERSION="$am" echo "Using aclocal: $AUTOMAKE_VERSION" break fi if which aclocal$am > /dev/null 2>&1; then AUTOMAKE_VERSION="$am" echo "Using aclocal: $AUTOMAKE_VERSION" break fi done fi fi export AUTOCONF_VERSION AUTOMAKE_VERSION # detect first glibtoolize then libtoolize if test "x$LIBTOOLIZE" = "x"; then LIBTOOLIZE=glibtoolize which $LIBTOOLIZE >/dev/null 2>&1 \ || LIBTOOLIZE=libtoolize fi # # Workarounds for libtoolize randomness - it does not update # the files if they exist, except it requires install-sh. # rm -f config.guess config.sub install-sh ltmain.sh libtool cp -p ${USUAL_DIR}/mk/install-sh . if ${LIBTOOLIZE} --help | grep "[-][-]install" > /dev/null; then ${LIBTOOLIZE} -i -f -q -c else ${LIBTOOLIZE} -c fi # drop ltmain.sh if libtool is not used grep -E 'LT_INIT|LIBTOOL' configure.ac > /dev/null \ || rm -f ltmain.sh # Now generate configure & config.h ${ACLOCAL} -I ${USUAL_DIR}/m4 grep AC_CONFIG_HEADER configure.ac > /dev/null \ && ${AUTOHEADER} ${AUTOCONF} # clean junk rm -rf autom4te.* aclocal* pgbouncer-1.7/lib/mk/antimake.txt0000664000175000017500000003216312511202014013770 00000000000000= antimake.mk(5) = == NAME == antimake - Minimal Automake syntax on plain GNU Make == DESCRIPTION == Antimake makes possible to use GNU Automake conventions to describe builds in ordinary Makefiles for GNU Make. It's main abstractions are target lists and target variables. Target list describes target type and where to install. Target variables give source files and additional flags for build. == EXAMPLE == ------------------- # target list bin_PROGRAMS = prog # target variables for 'prog' prog_SOURCES = prog.c prog.h prog_LDADD = libutil.a # target list noinst_LIBRARIES = libutil.a # target variables for 'libutil.a' libutil_a_SOURCES = util.c util.h # load Antimake include antimake.mk ------------------- == Terminology == Primary:: target type, describes how to build and install particular type of targets. Target:: a file that needs to be built and/or installed. Distribute:: Include file in source .tar.gz. Non-distributed files are skipped when building .tar.gz and are cleaned during `make distclean`. Source:: Source files are files that appear in `..._SOURCES` per-target variable. They are distributed by default. They may or may not result in object files. It's fine to put both `.h` and `.c` files into _SOURCES. == TARGET LISTS == Target lists are variables that contain file names that need to be built and installed. They are specially named so that the name also describes how they are built, how and where they will be installed. The target list name contains 3 parts, separated with underscore, in following order: 1. Optional flags. Flags are: `nodist`, `dist`, `nobase`, `base`. (Default: `base`, `nodist`) 2. Destination directory name. Destination directory called *bin* actual location is stored in Make variable `$(bindir)`. Some common values: `bin`, `lib`, `include`. There are more and the list can be extended. Special name `noinst` means the target file should not be installed. 3. Target type, also called "primary". This will describe how the target needs to be built. Common values: `PROGRAMS`, `LIBRARIES`, `DATA` For details, what the various values mean, see next sections. .Examples: ---------------- bin_PROGRAMS = prog1 prog2 # flags: base, nodist # dest: $(bindir) # type: PROGRAMS noinst_LIBRARIES = lib1.a lib2.a # flags: base, nodist # dest: noinst # type: LIBRARIES nobase_dist_doc_DATA = docs/README # flags: dist, nobase # dest: $(docdir)/docs # type: DATA ---------------- === Primaries === `PROGRAMS`:: executable programs, linked together from objects built from source files `LIBARIES`:: static libraries, linked together from objects built from source files `LTLIBRARIES`:: dynamic or static libraries, linked together from objects built from source files `HEADERS`:: header files, no default build method, the target files have `dist` flag by default. `MANS`:: man pages, no default build method, installed into manX subdir. `SCRIPTS`:: scripts, executable file, no default build method `DATA`:: data, non-executable file, no default build method === Target list flags === `dist`:: The target should be distributed with other sources. Default for `HEADERS` type, others have `nodist` by default. `nodist`:: Target is not distributed and should be cleaned with distclean. Default for all primaries, except `HEADERS`. `base`:: On install relative path is ignored, all files end up in destination directory. Always default. `nobase`:: On install relative path is kept. Eg: if `includedir=/usr/include` then `nobase_include_HEADERS=mylib/common.h` is installed to `/usr/include/mylib/common.h`. `noinst`:: Target is built as part of build process, but is not installed. `EXTRA`:: Targets in such list are not built, nor installed. Useful to make sure that sources for dynamically configured targets will end up in source tarball. Unlike other target list ariables, `EXTRA_` may contain targets already defined in other target lists, they will be filtered out from this list then. == Target variables == Only big targets take additional variables: `PROGRAMS`/`LIBRARIES`/`LTLIBRARIES`. `_SOURCES`:: All source files, *.c *.h *.cpp *.hpp. `nodist__SOURCES`:: Source files that should not be distributed. `EXTRA__SOURCES`:: In case tgt_SOURCES is dynamic, here is non-dynamic list of sources for distribution. Only dynamic sources need to be listed here. `_DEPENDENCIES`:: Add dependencies that need to be build before target build will start. `_CFLAGS`, `_CPPFLAGS`, `_LDFLAGS`, `_LIBTOOLFLAGS`:: Override corresponging AM_xx variable `_LDADD`:: Add dependencies that are used during linking. For PROGRAMS only. They will be added to linker command line. `_LIBADD`:: Add dependencies that are used during linking. For LIBRARIES/LTLIBRARIES only. They will be added to linker command line. `_AR`:: Overrides $(AR) $(ARFLAGS). For LIBRARIES only. .Example: ------------------- bin_PROGRAMS = prog prog_SOURCE = main.c util.c util.h prog_CFLAGS = $(GTK_CFLAGS) prog_LDADD = $(GTK_LIBS) ------------------- == Global variables == They can be set before `antimake.mk` inclusion to change build behaviour. EXTRA_DIST:: Additional files to include in source archive. CLEANFILES:: Additional files to `make clean`. DISTCLEANFILES:: Additional files to `make distclean`. MAINTAINERCLEANFILES:: Additional files to `make maintainer-clean`. SUBDIRS:: Subdirectories of current directory where Make needs to be recursively launched. If subdirectory `Makefile` is Antimake-base, it should set `SUBLOC`. SUBLOC:: Current diretory location in overall source tree. This can stay unset in top directory. Needed for subdirectiories entered with `SUBDIRS` to find its position in source tree. DIST_SUBDIRS:: Subdirs that only `make dist`, `make distclean` and `make maintainer-clean` will enter. EMBED_SUBDIRS:: Subdirectories that are built non-recursively: they need to contain `Makefile.am` that contains makefile-fragment with Antimake syntax that describes local targets using relative filenames. The fragment is included in main makefile and file and variable names are converted and merged with top-level targets. AM_FEATURES:: List of extensions to load. Extensions are Makefile fragments that are loaded before actual rules are generated, so they can change or add targets. === More details on EMBED_SUBDIRS === It acts like `include $(dir)/Makefile.am` for each directory, except it converts file and variable names. Example: --------------------- Makefile: EMBED_SUBDIRS = src src/Makefile.am: bin_PROGRAMS = hello hello_SOURCES = main.c hello_CPPFLAGS = -I./include --------------------- Conversion results as if top-level `Makefile` had contained following rows: ---------------------- bin_PROGRAMS += src/hello src_hello_SOURCES = src/main.c src_hello_CPPFLAGS = -I./src/include ---------------------- Variables, where file names are converted: * SUBDIRS, DIST_SUBDIRS, EMBED_SUBDIRS * DISTFILES, CLEANFILES, DISTCLEANFILES, MAINTAINERCLEANFILES * target lists * _SOURCES, _LDADD, _LIBADD Variables, where -L and -I flags are converted: * _CFLAGS * _CPPFLAGS * _LDFLAGS Makefile should be written in a way that those conversions would be enough. === Global variables for current location === * srcdir, builddir - relative path to source dir and build dir. * top_srcdir, top_builddir - relative path to top-level source and build dir. * abs_srcdir, abs_builddir - absolute path to source and build dir * abs_top_srcdir, abs_top_builddir - absolute path to top-level source and build dir * nosub_top_srcdir, nosub_top_builddir - relative path from top of builddir to srcdir and builddir. === Global variables that target can override === - AM_CPPFLAGS - AM_CFLAGS - AM_LDFLAGS - AM_LIBTOOLFLAGS - AM_DEFS - AM_MAKEFLAGS === Global variables from autoconf === These variables come usually from autoconf, but also have reasonable defaults: CC, DEFS, CPPFLAGS, CFLAGS, LDFLAGS, LIBS, LIBTOOL, LIBTOOLFLAGS, AR, ARFLAGS, RANLIB, CXX, CXXFLAGS, INSTALL, MKDIR_P, LN_S === Global variables for extending Antimake === AM_DIST_DEFAULT:: Default format(s) for `make dist` target. One or more of: `gzip`, `bzip2`, `xz`, `zip`. Default: `gzip`. AM_DESTINATIONS:: Additional directory names to consider as valid destinations. Expects corresponding `dir`-variable to be set. AM_SMALL_PRIMARIES:: Additional single-file primaries. (Builtin: HEADERS, SCRIPTS, DATA, MANS) AM_BIG_PRIMARIES:: Additional primaries built from objects. (Builtin: PROGRAMS, LIBRARIES, LTLIBRARIES) AM_LANGUAGES:: Additional language names. Antimake expects variables `AM_LANG_$(name)_SRCEXTS`, `AM_LANG_$(name)_COMPILE` and `AM_LANG_$(name)_LINK` to be set. === Variables for command-line usage === DESTDIR:: Relocate installation root. AM_TRACE:: Turns on function-call debug info. Can be set from command-line. === Hacking variables === GNUMAKE380, GNUMAKE381, GNUMAKE382:: If we have at least that version of GNU Make. GNUMAKE380 is always set, others may not be. If Makefile uses features from newer GNU Make it would be good idea to use those flags and error out with clear error message, instead having mysterious failures. === Libtool flags === Useful http://www.gnu.org/software/libtool/manual/html_node/Link-mode.html[Libtool] flags that can be put int tgt_LDFLAGS for a LTLIBRARY: * -export-dynamic * -export-symbols symfile * -export-symbols-regex regex * -module See libtool http://www.gnu.org/software/libtool/manual/html_node/Versioning.html["Versioning"] chapter about those: * -avoid-version * -version-info current[:revision[:age]] * -version-number major[:minor[:revision]] * -release major[:minor[:revision]] == Top-level pseudo-targets == === all === The default target when no other target is given on command-line. Builds all target files. ==== Simple targets ==== These are simple - either the file already exists, or the user needs to give build command. ==== Object-based targets ==== The targets in primaries PROGRAMS, LIBRARIES and LTLIBRARIES consist of multiple source files that need to be compiled into objects. Then the objects need to be linked into final target. The process is roughly following: . Dependencies are built (_LDADD, _LIBADD, _DEPENDENCIES). . Source list is filtered for extensions that can be compiled into object files, object file list is created based on them. The rest of files are used and dependencies for target, but otherwise ignored. . Object files are built. . Linker is picked based on source files - as there can be files in multiple languages, the most advanced language wins (the one that appears later in `AM_LANGUAGES`) . Final executable is linked. === install === Install all targets to their destination directories, which is mentioned in their target list variable name. Eg. `bin_PROGRAMS` will be installed to `$(bindir)`. If destination is named `noinst`, it will not be installed. If the flag `nobase` is given, the relative filename is kept, otherwise basename is taken and it will appear directly under destination directory. .Example: ------ include_HEADERS = func1.h lib/func2.h # Files will end up in: # $(includedir)/func1.h # $(includedir)/func2.h nobase_include_HEADERS = func1.h lib/func2.h # Files will end up in: # $(includedir)/func1.h # $(includedir)/lib/func2.h ------ === clean === - Remove files in `$(CLEANFILES)` - Remove built objects. - Remove target files, unless they are marked as `dist`. (Note: `HEADERS` primary is `dist` by default, all other are `nodist`) === distclean === - Remove files in `$(DISTCLEANFILES)` - Remove sources tagged with `nodist`. All sources as `dist` by default. === maintainer-clean === - Remove files in `$(MAINTAINERCLEANFILES)` === help === Describe top-level targets. === am-test === Regression test for low-level Antimake functions. === am-debug === Show Antimake internal state. == FEATURES == Done: - Big primaries: PROGRAMS, LIBRARIES, LTLIBRARIES - Small primaries: DATA, SCRIPTS, MANS, HEADERS - Flags: base nobase dist nodist noinst EXTRA - Target vars: SOURCES, CPPFLAGS, CFLAGS, LDFLAGS, LDADD/LIBADD - Separate build dir - Per-target objects - Languages: C, CXX - SUBDIRS, DIST_SUBDIRS - EMBED_SUBDIRS Todo: - Improve docs - Standardize and document how to extend - Deps with non-gcc? - Long if-s to support `O=` seems to break GNU Make 3.80. Drop `O=` or drop 3.80? Probably out of scope: - `make uninstall` - `make distcheck` - `make dist` from separate build dir - `install-(exec|data)-hook` - based on dir not primary - Default file list for `EXTRA_DIST`. (Problem: distclean / maintainer-clean) Definitely out of scope: - automake conditionals - automake extras (autoconf macros, ltdl) - automake nanny mode (gnu/gnits) == SEE ALSO == GNU Make Reference: http://www.gnu.org/software/make/manual/make.html#Quick-Reference[] Recursive Make Considered Harmful: http://miller.emu.id.au/pmiller/books/rmch/[] Paul's Rules of Makefiles: http://make.mad-scientist.us/rules.html[] Small BSD-ish build system: https://webkeks.org/hg/buildsys/[] GNU Make Standard Library: http://sourceforge.net/projects/gmsl/[] pgbouncer-1.7/lib/mk/amext-cxx.mk0000664000175000017500000000167012511203511013707 00000000000000 # # Support for C++ language # # - extensions: .cc, .cpp, cxx # - CXX, CXXFLAGS # - AM_CXXFLAGS, _CXXFLAGS # # autoconfigurable values ifneq ($(filter-out @%,@CXX@),) CXX = @CXX@ CXXFLAGS = @CXXFLAGS@ endif CXX ?= c++ CXXFLAGS ?= -O -g # fixme: add warning flags to CXXFLAGS CXXFLAGS += $(WFLAGS) # helper variables CXXLD ?= $(CXX) CXXCOMPILE ?= $(CXX) $(AM_DEFS) $(DEFS) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) CXXLINK ?= $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ # full compile command define AM_LANG_CXX_COMPILE $(E) "CXX" $< $(Q) $(LTCOMPILE) $(CXXCOMPILE) $(OBJDEPS) -c -o $@ $< endef # full link command define AM_LANG_CXX_LINK $(E) "CXXLD" $@ $(Q) $(LTLINK) $(CXXLINK) $^ $(AM_LIBS) $(LIBS) $(AM_LT_RPATH) endef # source file extensions for c++ AM_LANG_CXX_SRCEXTS = .cc .cpp cxx # register per-target variable AM_TARGET_VARIABLES += CXXFLAGS # register new language AM_LANGUAGES += CXX pgbouncer-1.7/lib/mk/amext-msvc.mk0000664000175000017500000000211112511203511014044 00000000000000# # Support for MSVC toolchain. # # Usage: # 1. Install coreutils (printf, tail) and make from gnuwin32. # 2. Make sure VC env variables are loaded (PATH) # SHELL = cmd.exe ShellQuote = "$(subst $$, \$$, $(subst ",\",$(subst \,\\,$(1))))" EXEEXT = .exe LIBEXT = .lib OBJEXT = .obj CC = cl -nologo CFLAGS = -O2 $(WFLAGS) WFLAGS = -W2 -w24013 CPP = $(CC) -E LDFLAGS = LIBS = -lws2_32 -ladvapi32 AR = lib ARFLAGS = -nologo LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -Fe$(call vcFixPath,$@) Printf = printf $(subst %,%%,$(1)) $(2) MKDIR_P = md MkDir = if not exist $(call vcFixPath,$(1)) $(MKDIR_P) $(call vcFixPath,$(1)) vcFixPath = $(subst /,\,$(1)) vcFixLibs = $(patsubst %.a,%.lib,$(patsubst -l%,%.lib,$(1))) vcFixAll = $(call vcFixPath,$(call vcFixLibs,$(1))) define AM_LANG_C_COMPILE $(E) "CC" $< $(Q) $(COMPILE) -c -Fo$(call vcFixPath,$@) $< | tail -n+2 endef define AM_LANG_C_LINK $(E) "CCLD" $@ $(Q) $(LINK) $(call vcFixAll,$^ $(AM_LIBS) $(LIBS)) $(AM_LT_RPATH) endef define ar_lib $(E) "LIB" $@ $(Q) $(AR) $(ARFLAGS) -out:$(call vcFixPath,$@) $^ endef pgbouncer-1.7/lib/mk/install-sh0000775000175000017500000003253712511202014013447 00000000000000#!/bin/sh # install - install a program, script, or datafile scriptversion=2009-04-28.21; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the # following copyright and license. # # Copyright (C) 1994 X Consortium # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the name of the X Consortium shall not # be used in advertising or otherwise to promote the sale, use or other deal- # ings in this Software without prior written authorization from the X Consor- # tium. # # # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent # `make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. nl=' ' IFS=" "" $nl" # set DOITPROG to echo to test this script # Don't use :- since 4.3BSD and earlier shells don't like it. doit=${DOITPROG-} if test -z "$doit"; then doit_exec=exec else doit_exec=$doit fi # Put in absolute file names if you don't have them in your path; # or use environment vars. chgrpprog=${CHGRPPROG-chgrp} chmodprog=${CHMODPROG-chmod} chownprog=${CHOWNPROG-chown} cmpprog=${CMPPROG-cmp} cpprog=${CPPROG-cp} mkdirprog=${MKDIRPROG-mkdir} mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} posix_glob='?' initialize_posix_glob=' test "$posix_glob" != "?" || { if (set -f) 2>/dev/null; then posix_glob= else posix_glob=: fi } ' posix_mkdir= # Desired mode of installed file. mode=0755 chgrpcmd= chmodcmd=$chmodprog chowncmd= mvcmd=$mvprog rmcmd="$rmprog -f" stripcmd= src= dst= dir_arg= dst_arg= copy_on_change=false no_target_directory= usage="\ Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE or: $0 [OPTION]... SRCFILES... DIRECTORY or: $0 [OPTION]... -t DIRECTORY SRCFILES... or: $0 [OPTION]... -d DIRECTORIES... In the 1st form, copy SRCFILE to DSTFILE. In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. In the 4th, create DIRECTORIES. Options: --help display this help and exit. --version display version info and exit. -c (ignored) -C install only if different (preserve the last data modification time) -d create directories instead of installing files. -g GROUP $chgrpprog installed files to GROUP. -m MODE $chmodprog installed files to MODE. -o USER $chownprog installed files to USER. -s $stripprog installed files. -t DIRECTORY install into DIRECTORY. -T report an error if DSTFILE is a directory. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG " while test $# -ne 0; do case $1 in -c) ;; -C) copy_on_change=true;; -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 case $mode in *' '* | *' '* | *' '* | *'*'* | *'?'* | *'['*) echo "$0: invalid mode: $mode" >&2 exit 1;; esac shift;; -o) chowncmd="$chownprog $2" shift;; -s) stripcmd=$stripprog;; -t) dst_arg=$2 shift;; -T) no_target_directory=true;; --version) echo "$0 $scriptversion"; exit $?;; --) shift break;; -*) echo "$0: invalid option: $1" >&2 exit 1;; *) break;; esac shift done if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. # Otherwise, the last argument is the destination. Remove it from $@. for arg do if test -n "$dst_arg"; then # $@ is not empty: it contains at least $arg. set fnord "$@" "$dst_arg" shift # fnord fi shift # arg dst_arg=$arg done fi if test $# -eq 0; then if test -z "$dir_arg"; then echo "$0: no input file specified." >&2 exit 1 fi # It's OK to call `install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi if test -z "$dir_arg"; then trap '(exit $?); exit' 1 2 13 15 # Set umask so as not to create temps with too-generous modes. # However, 'strip' requires both read and write access to temps. case $mode in # Optimize common cases. *644) cp_umask=133;; *755) cp_umask=22;; *[0-7]) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac fi for src do # Protect names starting with `-'. case $src in -*) src=./$src;; esac if test -n "$dir_arg"; then dst=$src dstdir=$dst test -d "$dstdir" dstdir_status=$? else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if test ! -f "$src" && test ! -d "$src"; then echo "$0: $src does not exist." >&2 exit 1 fi if test -z "$dst_arg"; then echo "$0: no destination specified." >&2 exit 1 fi dst=$dst_arg # Protect names starting with `-'. case $dst in -*) dst=./$dst;; esac # If destination is a directory, append the input filename; won't work # if double slashes aren't ignored. if test -d "$dst"; then if test -n "$no_target_directory"; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst dst=$dstdir/`basename "$src"` dstdir_status=0 else # Prefer dirname, but fall back on a substitute if dirname fails. dstdir=` (dirname "$dst") 2>/dev/null || expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$dst" : 'X\(//\)[^/]' \| \ X"$dst" : 'X\(//\)$' \| \ X"$dst" : 'X\(/\)' \| . 2>/dev/null || echo X"$dst" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q' ` test -d "$dstdir" dstdir_status=$? fi fi obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') # Create intermediate dirs using mode 755 as modified by the umask. # This is like FreeBSD 'install' as of 1997-10-28. umask=`umask` case $stripcmd.$umask in # Optimize common cases. *[2367][2367]) mkdir_umask=$umask;; .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; *[0-7]) mkdir_umask=`expr $umask + 22 \ - $umask % 100 % 40 + $umask % 20 \ - $umask % 10 % 4 + $umask % 2 `;; *) mkdir_umask=$umask,go-w;; esac # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then mkdir_mode=-m$mode else mkdir_mode= fi posix_mkdir=false case $umask in *[123567][0-7][0-7]) # POSIX mkdir -p sets u+wx bits regardless of umask, which # is incompatible with FreeBSD 'install' when (umask & 300) != 0. ;; *) tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 if (umask $mkdir_umask && exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 then if test -z "$dir_arg" || { # Check for POSIX incompatibilities with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or # other-writeable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. ls_ld_tmpdir=`ls -ld "$tmpdir"` case $ls_ld_tmpdir in d????-?r-*) different_mode=700;; d????-?--*) different_mode=755;; *) false;; esac && $mkdirprog -m$different_mode -p -- "$tmpdir" && { ls_ld_tmpdir_1=`ls -ld "$tmpdir"` test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" } } then posix_mkdir=: fi rmdir "$tmpdir/d" "$tmpdir" else # Remove any dirs left behind by ancient mkdir implementations. rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null fi trap '' 0;; esac;; esac if $posix_mkdir && ( umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else # The umask is ridiculous, or mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. case $dstdir in /*) prefix='/';; -*) prefix='./';; *) prefix='';; esac eval "$initialize_posix_glob" oIFS=$IFS IFS=/ $posix_glob set -f set fnord $dstdir shift $posix_glob set +f IFS=$oIFS prefixes= for d do test -z "$d" && continue prefix=$prefix$d if test -d "$prefix"; then prefixes= else if $posix_mkdir; then (umask=$mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 else case $prefix in *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; *) qprefix=$prefix;; esac prefixes="$prefixes '$qprefix'" fi fi prefix=$prefix/ done if test -n "$prefixes"; then # Don't fail if two instances are running concurrently. (umask $mkdir_umask && eval "\$doit_exec \$mkdirprog $prefixes") || test -d "$dstdir" || exit 1 obsolete_mkdir_used=true fi fi fi if test -n "$dir_arg"; then { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 else # Make a couple of temp file names in the proper directory. dsttmp=$dstdir/_inst.$$_ rmtmp=$dstdir/_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $cpprog $src $dsttmp" command. # { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && eval "$initialize_posix_glob" && $posix_glob set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && $posix_glob set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then rm -f "$dsttmp" else # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || # The rename failed, perhaps because mv can't rename something else # to itself, or perhaps because mv is so ancient that it does not # support -f. { # Now remove or move aside any old file at destination location. # We try this two ways since rm can't unlink itself on some # systems and the destination file might be busy for other # reasons. In this case, the final cleanup might fail but the new # file should still install successfully. { test ! -f "$dst" || $doit $rmcmd -f "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } } || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 } } && # Now rename the file to the real destination. $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 trap '' 0 fi done # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: pgbouncer-1.7/configure.ac0000664000175000017500000001023612635045106012562 00000000000000dnl Process this file with autoconf to produce a configure script. AC_INIT(pgbouncer, 1.7) AC_CONFIG_SRCDIR(src/janitor.c) AC_CONFIG_HEADER(lib/usual/config.h) AC_PREREQ([2.59]) dnl basic init AC_USUAL_INIT dnl Checks for programs. AC_USUAL_PROGRAM_CHECK PKG_PROG_PKG_CONFIG dnl check for rst2man AC_CHECK_PROGS(RST2MAN, rst2man) dnl check for windows tools if test "$PORTNAME" = "win32"; then AC_CHECK_TOOL([WINDRES], [windres]) AC_CHECK_TOOL([DLLWRAP], [dllwrap]) AC_CHECK_TOOL([DLLTOOL], [dlltool]) fi AC_CHECK_TOOL([STRIP], [strip]) dnl Checks for header files. AC_USUAL_HEADER_CHECK AC_CHECK_HEADERS([sys/resource.h sys/wait.h]) dnl Checks for typedefs, structures, and compiler characteristics. AC_USUAL_TYPE_CHECK dnl autoconf 2.59 does not have UINT macros nor docdir m4_ifdef([AC_TYPE_UINT8_T], [ AC_TYPE_UINT8_T AC_TYPE_UINT32_T AC_TYPE_UINT64_T ], [ datarootdir='${prefix}/share' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' AC_SUBST(datarootdir) AC_SUBST(docdir) ]) dnl Checks for library functions. AC_USUAL_FUNCTION_CHECK AC_SEARCH_LIBS(clock_gettime, rt) AC_SEARCH_LIBS(getsockname, socket) AC_SEARCH_LIBS(gethostbyname, nsl) AC_SEARCH_LIBS(hstrerror, resolv) AC_CHECK_FUNCS(lstat) dnl Find libevent AC_USUAL_LIBEVENT ## ## DNS backend ## # make sure all vars are set use_cares=auto use_udns=no use_evdns=no dnl Find c-ares AC_MSG_CHECKING([whether to use c-ares for DNS lookups]) AC_ARG_WITH(cares, AC_HELP_STRING([--with-cares=prefix],[Specify where c-ares is installed]), [ if test "$withval" = "no"; then use_cares=no elif test "$withval" = "yes"; then use_cares=yes CARES_CFLAGS="" CARES_LIBS="-lcares" else use_cares=yes CARES_CFLAGS="-I$withval/include" CARES_LIBS="-L$withval/lib -lcares" fi ], []) AC_MSG_RESULT([$use_cares]) if test "$use_cares" = "auto"; then PKG_CHECK_MODULES(CARES, [libcares >= 1.6.0], [use_cares=yes], [use_cares=no]) fi if test "$use_cares" = "yes"; then AC_DEFINE(USE_CARES, 1, [Use c-ares for name resolution.]) # does it support SOA parse tmp_CFLAGS="$CFLAGS" tmp_LIBS="$LIBS" CFLAGS="$CARES_CFLAGS $CFLAGS" LIBS="$CARES_LIBS $LIBS" AC_CHECK_FUNCS(ares_parse_soa_reply) LIBS="$tmp_LIBS" CFLAGS="$tmp_CFLAGS" else dnl Find libudns AC_MSG_CHECKING([whether to use libudns]) AC_ARG_WITH(udns, AC_HELP_STRING([--with-udns=prefix],[Specify where udns is installed]), [ if test "$withval" = "no"; then use_udns=no elif test "$withval" = "yes"; then use_udns=yes else use_udns=yes CPPFLAGS="$CPPFLAGS -I$withval/include" LDFLAGS="$LDFLAGS -L$withval/lib" fi ], []) AC_MSG_RESULT([$use_udns]) if test "$use_udns" = "yes"; then AC_DEFINE(USE_UDNS, 1, [Use UDNS for name resolution.]) LIBS="-ludns $LIBS" AC_MSG_CHECKING([whether libudns is available]) AC_LINK_IFELSE([AC_LANG_SOURCE([ #include #include #include #include int main(void) { struct dns_ctx *ctx = NULL; dns_init(ctx, 0); dns_reset(ctx); } ])], [AC_MSG_RESULT([found])], [AC_MSG_ERROR([not found, cannot proceed])]) else # !udns dnl On libevent 2.x use evdns by default if test "$ac_cv_func_evdns_base_new" = "yes"; then use_evdns=yes fi dnl Allow user to override the decision AC_ARG_ENABLE(evdns, AC_HELP_STRING([--enable-evdns],[Use libevent for DNS lookups (default on libevent 2.x)]), [use_evdns=$enableval]) AC_MSG_CHECKING([whether to use libevent for DNS lookups]) if test "$use_evdns" = "yes"; then AC_DEFINE(USE_EVDNS, 1, [Use libevent for DNS lookups.]) AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi dnl Check if need getaddinfo_a compat if test "$use_udns.$use_cares.$use_evdns" = "no.no.no"; then AC_USUAL_GETADDRINFO_A fi fi # !udns fi # !cares ## end of DNS AC_USUAL_TLS AC_USUAL_DEBUG AC_USUAL_CASSERT AC_USUAL_WERROR dnl Output findings AC_OUTPUT([config.mak]) dnl If separate build dir, link Makefile over test -f Makefile || { echo "Linking Makefile" ln -s $srcdir/Makefile } echo "" echo "Results" echo " c-ares = $use_cares" echo " evdns = $use_evdns" echo " udns = $use_udns" echo " tls = $tls_support" echo "" pgbouncer-1.7/Makefile0000664000175000017500000000725312630310713011733 00000000000000 include config.mak bin_PROGRAMS = pgbouncer pgbouncer_SOURCES = \ src/admin.c \ src/client.c \ src/dnslookup.c \ src/hba.c \ src/janitor.c \ src/loader.c \ src/main.c \ src/objects.c \ src/pktbuf.c \ src/pooler.c \ src/proto.c \ src/sbuf.c \ src/server.c \ src/stats.c \ src/system.c \ src/takeover.c \ src/util.c \ src/varcache.c \ include/admin.h \ include/bouncer.h \ include/client.h \ include/dnslookup.h \ include/hba.h \ include/iobuf.h \ include/janitor.h \ include/loader.h \ include/objects.h \ include/pktbuf.h \ include/pooler.h \ include/proto.h \ include/sbuf.h \ include/server.h \ include/stats.h \ include/system.h \ include/takeover.h \ include/util.h \ include/varcache.h pgbouncer_CPPFLAGS = -Iinclude $(CARES_CFLAGS) $(TLS_CPPFLAGS) # include libusual sources directly AM_FEATURES = libusual pgbouncer_EMBED_LIBUSUAL = 1 # docs to install as-is dist_doc_DATA = README.rst NEWS.rst etc/pgbouncer.ini etc/userlist.txt DISTCLEANFILES = config.mak config.status lib/usual/config.h config.log DIST_SUBDIRS = doc test dist_man_MANS = doc/pgbouncer.1 doc/pgbouncer.5 # files in tgz EXTRA_DIST = AUTHORS COPYRIGHT Makefile config.mak.in config.sub config.guess \ install-sh autogen.sh configure configure.ac \ debian/compat debian/changelog debian/control debian/rules debian/copyright \ etc/mkauth.py etc/example.debian.init.sh \ win32/Makefile \ $(LIBUSUAL_DIST) # libusual files (FIXME: list should be provided by libusual...) LIBUSUAL_DIST = $(filter-out %/config.h, $(wildcard \ lib/usual/*.[chg] \ lib/usual/*/*.[ch] \ lib/m4/*.m4 \ lib/usual/config.h.in \ lib/mk/*.mk \ lib/mk/antimake.mk lib/mk/antimake.txt \ lib/mk/install-sh lib/mk/std-autogen.sh \ lib/README lib/COPYRIGHT \ lib/find_modules.sh )) ifeq ($(enable_debug),yes) CPPFLAGS += -DDBGVER="\"compiled by <$${USER}@`hostname`> at `date '+%Y-%m-%d %H:%M:%S'`\"" endif # # win32 # pgbouncer_LDFLAGS := $(TLS_LDFLAGS) pgbouncer_LDADD := $(CARES_LIBS) $(TLS_LIBS) $(LIBS) LIBS := EXTRA_pgbouncer_SOURCES = win32/win32support.c win32/win32support.h EXTRA_PROGRAMS = pgbevent ifeq ($(PORTNAME),win32) pgbouncer_CPPFLAGS += -Iwin32 pgbouncer_SOURCES += $(EXTRA_pgbouncer_SOURCES) bin_PROGRAMS += pgbevent endif pgbevent_SOURCES = win32/pgbevent.c win32/eventmsg.rc \ win32/eventmsg.mc win32/MSG00001.bin pgbevent_EXT = .dll pgbevent_LINK = $(CC) -shared -Wl,--export-all-symbols -Wl,--add-stdcall-alias -o $@ $^ # .rc->.o AM_LANGUAGES = RC AM_LANG_RC_SRCEXTS = .rc AM_LANG_RC_COMPILE = $(WINDRES) $< -o $@ --include-dir=$(srcdir)/win32 AM_LANG_RC_LINK = false # # now load antimake # USUAL_DIR = lib abs_top_srcdir ?= $(CURDIR) include $(abs_top_srcdir)/lib/mk/antimake.mk config.mak: @echo "Please run ./configure" @exit 1 deb: debuild -b -us -uc w32arch = i686-w64-mingw32 w32zip = pgbouncer-$(PACKAGE_VERSION)-win32.zip zip: configure clean rm -rf buildexe mkdir buildexe cd buildexe \ && ../configure --host=$(w32arch) --disable-debug \ --without-openssl \ --without-cares \ --with-libevent=/opt/apps/win32 --enable-evdns \ && make \ && $(w32arch)-strip pgbouncer.exe pgbevent.dll \ && zip pgbouncer.zip pgbouncer.exe pgbevent.dll doc/*.html zip -l buildexe/pgbouncer.zip etc/pgbouncer.ini etc/userlist.txt mv buildexe/pgbouncer.zip $(w32zip) zip-up: $(w32zip) rsync $(w32zip) pgf:web/pgbouncer/htdocs/win32/ tgz = pgbouncer-$(PACKAGE_VERSION).tar.gz tgz-up: $(tgz) rsync $(tgz) pgf:web/pgbouncer/htdocs/testing/ .PHONY: tags tags: ctags src/*.c include/*.h lib/usual/*.[ch] htmls: for f in *.rst doc/*.rst; do \ mkdir -p html && rst2html $$f > html/`basename $$f`.html; \ done doc/pgbouncer.1 doc/pgbouncer.5: $(MAKE) -C doc pgbouncer-1.7/config.mak.in0000664000175000017500000000273412572127367012656 00000000000000PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_VERSION = @PACKAGE_VERSION@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PORTNAME = @PORTNAME@ EXEEXT = @EXEEXT@ HAVE_CC_DEPFLAG = @HAVE_CC_DEPFLAG@ CC = @CC@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CFLAGS = @CFLAGS@ DEFS = @DEFS@ WFLAGS = @WFLAGS@ CXX = @CXX@ CXXFLAGS = @CXXFLAGS@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBS = @LIBS@ AR = @AR@ ARFLAGS = @ARFLAGS@ RANLIB = @RANLIB@ LIBTOOL = @LIBTOOL@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_DATA = @INSTALL_DATA@ MKDIR_P = @MKDIR_P@ SED = @SED@ AWK = @AWK@ GREP = @GREP@ EGREP = @EGREP@ STRIP = @STRIP@ prefix = @prefix@ exec_prefix = @exec_prefix@ bindir = @bindir@ includedir = @includedir@ sbindir = @sbindir@ libexecdir = @libexecdir@ datarootdir = @datarootdir@ datadir = @datadir@ sysconfdir = @sysconfdir@ docdir = @docdir@ mandir = @mandir@ libdir = @libdir@ localedir = @localedir@ pkgdatadir = @pkgdatadir@ pkgconfigdir = @pkgconfigdir@ abs_top_srcdir ?= @abs_top_srcdir@ abs_top_builddir ?= @abs_top_builddir@ nosub_top_srcdir ?= @top_srcdir@ nosub_top_builddir ?= @top_builddir@ CARES_CFLAGS = @CARES_CFLAGS@ CARES_LIBS = @CARES_LIBS@ TLS_CPPFLAGS = @TLS_CPPFLAGS@ TLS_LDFLAGS = @TLS_LDFLAGS@ TLS_LIBS = @TLS_LIBS@ RST2MAN = @RST2MAN@ DLLWRAP = @DLLWRAP@ DLLTOOL = @DLLTOOL@ WINDRES = @WINDRES@ enable_debug = @enable_debug@ have_libevent = @have_libevent@ pgbouncer-1.7/configure0000775000175000017500000074012312635051442012210 00000000000000#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.68 for pgbouncer 1.7. # # # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, # 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software # Foundation, Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 test \$(( 1 + 1 )) = 2 || exit 1" if (eval "$as_required") 2>/dev/null; then : as_have_required=yes else as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir/$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : CONFIG_SHELL=$as_shell as_have_required=yes if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : break 2 fi fi done;; esac as_found=false done $as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : CONFIG_SHELL=$SHELL as_have_required=yes fi; } IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV export CONFIG_SHELL case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec "$CONFIG_SHELL" $as_opts "$as_myself" ${1+"$@"} fi if test x$as_have_required = xno; then : $as_echo "$0: This script requires a shell more modern than all" $as_echo "$0: the shells that I found on your system." if test x${ZSH_VERSION+set} = xset ; then $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org about your system, $0: including any error possibly output before this $0: message. Then install a modern shell, or manually run $0: the script under such a shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -p'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -p' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -p' fi else as_ln_s='cp -p' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi if test -x / >/dev/null 2>&1; then as_test_x='test -x' else if ls -dL / >/dev/null 2>&1; then as_ls_L_option=L else as_ls_L_option= fi as_test_x=' eval sh -c '\'' if test -d "$1"; then test -d "$1/."; else case $1 in #( -*)set "./$1";; esac; case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( ???[sx]*):;;*)false;;esac;fi '\'' sh ' fi as_executable_p=$as_test_x # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='pgbouncer' PACKAGE_TARNAME='pgbouncer' PACKAGE_VERSION='1.7' PACKAGE_STRING='pgbouncer 1.7' PACKAGE_BUGREPORT='' PACKAGE_URL='' ac_unique_file="src/janitor.c" # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef STDC_HEADERS # include # include #else # ifdef HAVE_STDLIB_H # include # endif #endif #ifdef HAVE_STRING_H # if !defined STDC_HEADERS && defined HAVE_MEMORY_H # include # endif # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_subst_vars='LTLIBOBJS LIBOBJS enable_debug TLS_LIBS TLS_LDFLAGS TLS_CPPFLAGS tls_support PTHREAD_CFLAGS PTHREAD_LIBS PTHREAD_CC acx_pthread_config host_os host_vendor host_cpu host build_os build_vendor build_cpu build CARES_LIBS CARES_CFLAGS have_libevent DLLTOOL DLLWRAP WINDRES RST2MAN PKG_CONFIG_LIBDIR PKG_CONFIG_PATH PKG_CONFIG ARFLAGS AR RANLIB STRIP SED MKDIR_P AWK EGREP GREP LN_S INSTALL_DATA INSTALL_SCRIPT INSTALL_PROGRAM WFLAGS HAVE_CC_DEPFLAG CPP OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC pkgconfigdir pkgdatadir PORTNAME target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking enable_largefile with_libevent with_cares with_udns enable_evdns with_openssl with_root_ca_file enable_debug enable_cassert enable_werror ' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS CPPFLAGS CPP PKG_CONFIG PKG_CONFIG_PATH PKG_CONFIG_LIBDIR CARES_CFLAGS CARES_LIBS' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe $as_echo "$as_me: WARNING: if you wanted to set the --build type, don't use --host. If a cross compiler is detected then cross compile mode will be used" >&2 elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures pgbouncer 1.7 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/pgbouncer] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF System types: --build=BUILD configure for building on BUILD [guessed] --host=HOST cross-compile to build programs to run on HOST [BUILD] _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of pgbouncer 1.7:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --disable-largefile omit support for large files --enable-evdns Use libevent for DNS lookups (default on libevent 2.x) --disable-debug strip binary --enable-cassert turn on assert checking in code --enable-werror add -Werror to CFLAGS Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-libevent=prefix Specify where libevent is installed --with-cares=prefix Specify where c-ares is installed --with-udns=prefix Specify where udns is installed --with-openssl=prefix Specify where OpenSSL is installed --with-root-ca-file=cafile Specify where root CA certs are. Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory CPP C preprocessor PKG_CONFIG path to pkg-config utility PKG_CONFIG_PATH directories to add to pkg-config's search path PKG_CONFIG_LIBDIR path overriding pkg-config's built-in search path CARES_CFLAGS C compiler flags for CARES, overriding pkg-config CARES_LIBS linker flags for CARES, overriding pkg-config Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to the package provider. _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for guested configure. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF pgbouncer configure 1.7 generated by GNU Autoconf 2.68 Copyright (C) 2010 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_c_try_cpp LINENO # ---------------------- # Try to preprocess conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_cpp () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } > conftest.i && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_cpp # ac_fn_c_try_link LINENO # ----------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || $as_test_x conftest$ac_exeext }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_link # ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists, giving a warning if it cannot be compiled using # the include files in INCLUDES and setting the cache variable VAR # accordingly. ac_fn_c_check_header_mongrel () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if eval \${$3+:} false; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } else # Is the header compilable? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 $as_echo_n "checking $2 usability... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_header_compiler=yes else ac_header_compiler=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 $as_echo "$ac_header_compiler" >&6; } # Is the header present? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 $as_echo_n "checking $2 presence... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include <$2> _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : ac_header_preproc=yes else ac_header_preproc=no fi rm -f conftest.err conftest.i conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 $as_echo "$ac_header_preproc" >&6; } # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( yes:no: ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 $as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ;; no:yes:* ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 $as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 $as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 $as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 $as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else eval "$3=\$ac_header_compiler" fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_mongrel # ac_fn_c_try_run LINENO # ---------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. Assumes # that executables *can* be run. ac_fn_c_try_run () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then : ac_retval=0 else $as_echo "$as_me: program exited with status $ac_status" >&5 $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=$ac_status fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_run # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile # ac_fn_c_check_type LINENO TYPE VAR INCLUDES # ------------------------------------------- # Tests whether TYPE exists after having included INCLUDES, setting cache # variable VAR accordingly. ac_fn_c_check_type () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else eval "$3=no" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { if (sizeof ($2)) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { if (sizeof (($2))) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else eval "$3=yes" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_type # ac_fn_c_find_uintX_t LINENO BITS VAR # ------------------------------------ # Finds an unsigned integer type with width BITS, setting cache variable VAR # accordingly. ac_fn_c_find_uintX_t () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uint$2_t" >&5 $as_echo_n "checking for uint$2_t... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else eval "$3=no" # Order is important - never check a type that is potentially smaller # than half of the expected target width. for ac_type in uint$2_t 'unsigned int' 'unsigned long int' \ 'unsigned long long int' 'unsigned short int' 'unsigned char'; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int main () { static int test_array [1 - 2 * !((($ac_type) -1 >> ($2 / 2 - 1)) >> ($2 / 2 - 1) == 3)]; test_array [0] = 0 ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : case $ac_type in #( uint$2_t) : eval "$3=yes" ;; #( *) : eval "$3=\$ac_type" ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if eval test \"x\$"$3"\" = x"no"; then : else break fi done fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_find_uintX_t # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- # Tests whether FUNC exists, setting the cache variable VAR accordingly ac_fn_c_check_func () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $2 (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef $2 /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $2 (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$2 || defined __stub___$2 choke me #endif int main () { return $2 (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_func # ac_fn_c_check_decl LINENO SYMBOL VAR INCLUDES # --------------------------------------------- # Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR # accordingly. ac_fn_c_check_decl () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack as_decl_name=`echo $2|sed 's/ *(.*//'` as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5 $as_echo_n "checking whether $as_decl_name is declared... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { #ifndef $as_decl_name #ifdef __cplusplus (void) $as_decl_use; #else (void) $as_decl_name; #endif #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_decl cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by pgbouncer $as_me 1.7, which was generated by GNU Autoconf 2.68. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. $as_echo "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo $as_echo "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo $as_echo "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then $as_echo "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then $as_echo "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && $as_echo "$as_me: caught signal $ac_signal" $as_echo "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h $as_echo "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_URL "$PACKAGE_URL" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. ac_site_file1=NONE ac_site_file2=NONE if test -n "$CONFIG_SITE"; then # We do not want a PATH search for config.site. case $CONFIG_SITE in #(( -*) ac_site_file1=./$CONFIG_SITE;; */*) ac_site_file1=$CONFIG_SITE;; *) ac_site_file1=./$CONFIG_SITE;; esac elif test "x$prefix" != xNONE; then ac_site_file1=$prefix/share/config.site ac_site_file2=$prefix/etc/config.site else ac_site_file1=$ac_default_prefix/share/config.site ac_site_file2=$ac_default_prefix/etc/config.site fi for ac_site_file in "$ac_site_file1" "$ac_site_file2" do test "x$ac_site_file" = xNONE && continue if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 $as_echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 $as_echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 $as_echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 $as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 $as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 $as_echo "$as_me: former value: \`$ac_old_val'" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 $as_echo "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_config_headers="$ac_config_headers lib/usual/config.h" # if building separately from srcdir, write top-level makefile if test "$srcdir" != "."; then echo "include $srcdir/Makefile" > Makefile fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking target host type" >&5 $as_echo_n "checking target host type... " >&6; } xhost="$host_alias" if test "x$xhost" = "x"; then xhost=`uname -s` fi case "$xhost" in *cygwin* | *mingw* | *pw32* | *MINGW*) LIBS="$LIBS -lws2_32" PORTNAME=win32;; *) PORTNAME=unix ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PORTNAME" >&5 $as_echo "$PORTNAME" >&6; } if test "$PORTNAME" = "win32"; then $as_echo "#define WIN32_LEAN_AND_MEAN 1" >>confdefs.h $as_echo "#define WINVER 0x0501" >>confdefs.h else $as_echo "#define _GNU_SOURCE 1" >>confdefs.h fi pkgdatadir='${datarootdir}'/${PACKAGE_TARNAME} pkgconfigdir='${libdir}/pkgconfig' ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 $as_echo_n "checking whether the C compiler works... " >&6; } ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else ac_file='' fi if test -z "$ac_file"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 $as_echo_n "checking for C compiler default output file name... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 $as_echo "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 $as_echo_n "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 $as_echo "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 $as_echo_n "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 $as_echo "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 $as_echo_n "checking for suffix of object files... " >&6; } if ${ac_cv_objext+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 $as_echo "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } if ${ac_cv_c_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 $as_echo "$ac_cv_c_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } if ${ac_cv_prog_cc_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes else CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 $as_echo "$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_aux_dir= for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do if test -f "$ac_dir/install-sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install-sh -c" break elif test -f "$ac_dir/install.sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install.sh -c" break elif test -f "$ac_dir/shtool"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/shtool install -c" break fi done if test -z "$ac_aux_dir"; then as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 fi # These three variables are undocumented and unsupported, # and are intended to be withdrawn in a future Autoconf release. # They can cause serious problems if a builder's source tree is in a directory # whose full name contains unusual characters. ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: # SysV /etc/install, /usr/sbin/install # SunOS /usr/etc/install # IRIX /sbin/install # AIX /bin/install # AmigaOS /C/install, which installs bootblocks on floppy discs # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag # AFS /usr/afsws/bin/install, which mishandles nonexistent args # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # OS/2's system install, which has a completely different semantic # ./install, which can be erroneously created by make from ./install.sh. # Reject install programs that cannot install multiple files. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 $as_echo_n "checking for a BSD-compatible install... " >&6; } if test -z "$INSTALL"; then if ${ac_cv_path_install+:} false; then : $as_echo_n "(cached) " >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. # Account for people who put trailing slashes in PATH elements. case $as_dir/ in #(( ./ | .// | /[cC]/* | \ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ /usr/ucb/* ) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. # Don't use installbsd from OSF since it installs stuff as root # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then if test $ac_prog = install && grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. : elif test $ac_prog = install && grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # program-specific install script used by HP pwplus--don't use. : else rm -rf conftest.one conftest.two conftest.dir echo one > conftest.one echo two > conftest.two mkdir conftest.dir if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && test -s conftest.one && test -s conftest.two && test -s conftest.dir/conftest.one && test -s conftest.dir/conftest.two then ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" break 3 fi fi fi done done ;; esac done IFS=$as_save_IFS rm -rf conftest.one conftest.two conftest.dir fi if test "${ac_cv_path_install+set}" = set; then INSTALL=$ac_cv_path_install else # As a last resort, use the slow shell script. Don't cache a # value for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. INSTALL=$ac_install_sh fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 $as_echo "$INSTALL" >&6; } # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' { $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 $as_echo_n "checking for grep that handles long lines and -e... " >&6; } if ${ac_cv_path_GREP+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$GREP"; then ac_path_GREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in grep ggrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in *GNU*) ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'GREP' >> "conftest.nl" "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_GREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_GREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_GREP"; then as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_GREP=$GREP fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 $as_echo "$ac_cv_path_GREP" >&6; } GREP="$ac_cv_path_GREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5 $as_echo_n "checking for a thread-safe mkdir -p... " >&6; } if test -z "$MKDIR_P"; then if ${ac_cv_path_mkdir+:} false; then : $as_echo_n "(cached) " >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in mkdir gmkdir; do for ac_exec_ext in '' $ac_executable_extensions; do { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; } || continue case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( 'mkdir (GNU coreutils) '* | \ 'mkdir (coreutils) '* | \ 'mkdir (fileutils) '4.1*) ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext break 3;; esac done done done IFS=$as_save_IFS fi test -d ./--version && rmdir ./--version if test "${ac_cv_path_mkdir+set}" = set; then MKDIR_P="$ac_cv_path_mkdir -p" else # As a last resort, use the slow shell script. Don't cache a # value for MKDIR_P within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. MKDIR_P="$ac_install_sh -d" fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 $as_echo "$MKDIR_P" >&6; } case $ac_cv_prog_cc_stdc in #( no) : ac_cv_prog_cc_c99=no; ac_cv_prog_cc_c89=no ;; #( *) : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C99" >&5 $as_echo_n "checking for $CC option to accept ISO C99... " >&6; } if ${ac_cv_prog_cc_c99+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c99=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include #include // Check varargs macros. These examples are taken from C99 6.10.3.5. #define debug(...) fprintf (stderr, __VA_ARGS__) #define showlist(...) puts (#__VA_ARGS__) #define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) static void test_varargs_macros (void) { int x = 1234; int y = 5678; debug ("Flag"); debug ("X = %d\n", x); showlist (The first, second, and third items.); report (x>y, "x is %d but y is %d", x, y); } // Check long long types. #define BIG64 18446744073709551615ull #define BIG32 4294967295ul #define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) #if !BIG_OK your preprocessor is broken; #endif #if BIG_OK #else your preprocessor is broken; #endif static long long int bignum = -9223372036854775807LL; static unsigned long long int ubignum = BIG64; struct incomplete_array { int datasize; double data[]; }; struct named_init { int number; const wchar_t *name; double average; }; typedef const char *ccp; static inline int test_restrict (ccp restrict text) { // See if C++-style comments work. // Iterate through items via the restricted pointer. // Also check for declarations in for loops. for (unsigned int i = 0; *(text+i) != '\0'; ++i) continue; return 0; } // Check varargs and va_copy. static void test_varargs (const char *format, ...) { va_list args; va_start (args, format); va_list args_copy; va_copy (args_copy, args); const char *str; int number; float fnumber; while (*format) { switch (*format++) { case 's': // string str = va_arg (args_copy, const char *); break; case 'd': // int number = va_arg (args_copy, int); break; case 'f': // float fnumber = va_arg (args_copy, double); break; default: break; } } va_end (args_copy); va_end (args); } int main () { // Check bool. _Bool success = false; // Check restrict. if (test_restrict ("String literal") == 0) success = true; char *restrict newvar = "Another string"; // Check varargs. test_varargs ("s, d' f .", "string", 65, 34.234); test_varargs_macros (); // Check flexible array members. struct incomplete_array *ia = malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); ia->datasize = 10; for (int i = 0; i < ia->datasize; ++i) ia->data[i] = i * 1.234; // Check named initializers. struct named_init ni = { .number = 34, .name = L"Test wide string", .average = 543.34343, }; ni.number = 58; int dynamic_array[ni.number]; dynamic_array[ni.number - 1] = 543; // work around unused variable warnings return (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == 'x' || dynamic_array[ni.number - 1] != 543); ; return 0; } _ACEOF for ac_arg in '' -std=gnu99 -std=c99 -c99 -AC99 -xc99=all -qlanglvl=extc99 do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c99=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c99" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c99" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c99" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 $as_echo "$ac_cv_prog_cc_c99" >&6; } ;; esac if test "x$ac_cv_prog_cc_c99" != xno; then : ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99 else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89 else ac_cv_prog_cc_stdc=no fi fi ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO Standard C" >&5 $as_echo_n "checking for $CC option to accept ISO Standard C... " >&6; } if ${ac_cv_prog_cc_stdc+:} false; then : $as_echo_n "(cached) " >&6 fi case $ac_cv_prog_cc_stdc in #( no) : { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; #( '') : { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; #( *) : { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_stdc" >&5 $as_echo "$ac_cv_prog_cc_stdc" >&6; } ;; esac ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 $as_echo_n "checking how to run the C preprocessor... " >&6; } # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if ${ac_cv_prog_CPP+:} false; then : $as_echo_n "(cached) " >&6 else # Double quotes because CPP needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" do ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi done ac_cv_prog_CPP=$CPP fi CPP=$ac_cv_prog_CPP else ac_cv_prog_CPP=$CPP fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 $as_echo "$CPP" >&6; } ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports __func__" >&5 $as_echo_n "checking whether compiler supports __func__... " >&6; } if ${pgac_cv_funcname_func+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { printf("%s\n", __func__); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : pgac_cv_funcname_func=yes else pgac_cv_funcname_func=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_funcname_func" >&5 $as_echo "$pgac_cv_funcname_func" >&6; } if test x"$pgac_cv_funcname_func" = xyes ; then $as_echo "#define HAVE_FUNCNAME__FUNC 1" >>confdefs.h fi if test "$GCC" = "yes"; then old_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS -Wl,--as-needed" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether linker supports --as-needed" >&5 $as_echo_n "checking whether linker supports --as-needed... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main(void) { return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } LDFLAGS="$old_LDFLAGS" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports dependency generation" >&5 $as_echo_n "checking whether compiler supports dependency generation... " >&6; } old_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -MD -MP -MT conftest.o -MF conftest.o.d" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ void foo(void){} _ACEOF if ac_fn_c_try_compile "$LINENO"; then : HAVE_CC_DEPFLAG=yes else HAVE_CC_DEPFLAG=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext rm -f conftest.d CFLAGS="$old_CFLAGS" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $HAVE_CC_DEPFLAG" >&5 $as_echo "$HAVE_CC_DEPFLAG" >&6; } WFLAGS="" if test x"$GCC" = xyes; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working warning switches" >&5 $as_echo_n "checking for working warning switches... " >&6; } good_CFLAGS="$CFLAGS" flags="-Wall -Wextra" # turn off noise from Wextra flags="$flags -Wno-unused-parameter -Wno-missing-field-initializers" # Wextra does not turn those on? flags="$flags -Wmissing-prototypes -Wpointer-arith -Wendif-labels" flags="$flags -Wdeclaration-after-statement -Wold-style-definition" flags="$flags -Wstrict-prototypes -Wundef -Wformat=2" flags="$flags -Wuninitialized" for f in $flags; do CFLAGS="$good_CFLAGS $WFLAGS $f" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ void foo(void){} _ACEOF if ac_fn_c_try_compile "$LINENO"; then : WFLAGS="$WFLAGS $f" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext done # avoid -Wextra if missing-field.initializers does not work echo "$WFLAGS" | grep missing-field-initializers > /dev/null \ || WFLAGS=`echo "$WFLAGS"|sed 's/ -Wextra//'` CFLAGS="$good_CFLAGS" { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5 $as_echo "done" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 $as_echo_n "checking whether ln -s works... " >&6; } LN_S=$as_ln_s if test "$LN_S" = "ln -s"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 $as_echo "no, using $LN_S" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 $as_echo_n "checking for egrep... " >&6; } if ${ac_cv_path_EGREP+:} false; then : $as_echo_n "(cached) " >&6 else if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 then ac_cv_path_EGREP="$GREP -E" else if test -z "$EGREP"; then ac_path_EGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in *GNU*) ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'EGREP' >> "conftest.nl" "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_EGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_EGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_EGREP"; then as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_EGREP=$EGREP fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 $as_echo "$ac_cv_path_EGREP" >&6; } EGREP="$ac_cv_path_EGREP" for ac_prog in gawk mawk nawk awk do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_AWK+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$AWK"; then ac_cv_prog_AWK="$AWK" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_AWK="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AWK=$ac_cv_prog_AWK if test -n "$AWK"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 $as_echo "$AWK" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$AWK" && break done { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5 $as_echo_n "checking for a sed that does not truncate output... " >&6; } if ${ac_cv_path_SED+:} false; then : $as_echo_n "(cached) " >&6 else ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ for ac_i in 1 2 3 4 5 6 7; do ac_script="$ac_script$as_nl$ac_script" done echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed { ac_script=; unset ac_script;} if test -z "$SED"; then ac_path_SED_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in sed gsed; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_SED="$as_dir/$ac_prog$ac_exec_ext" { test -f "$ac_path_SED" && $as_test_x "$ac_path_SED"; } || continue # Check for GNU ac_path_SED and select it if it is found. # Check for GNU $ac_path_SED case `"$ac_path_SED" --version 2>&1` in *GNU*) ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo '' >> "conftest.nl" "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_SED_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_SED="$ac_path_SED" ac_path_SED_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_SED_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_SED"; then as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5 fi else ac_cv_path_SED=$SED fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5 $as_echo "$ac_cv_path_SED" >&6; } SED="$ac_cv_path_SED" rm -f conftest.sed case "$ac_install_sh" in ./*) ac_install_sh="`pwd`/${ac_install_sh}" ;; ../*) ac_install_sh="`pwd`/${ac_install_sh}" ;; esac case "$INSTALL" in ./*) INSTALL="`pwd`/${INSTALL}" ;; ../*) INSTALL="`pwd`/${INSTALL}" ;; esac case "$MKDIR_P" in ./*) MKDIR_P="`pwd`/${MKDIR_P}" ;; ../*) MKDIR_P="`pwd`/${MKDIR_P}" ;; esac if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. set dummy ${ac_tool_prefix}strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$STRIP"; then ac_cv_prog_STRIP="$STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_STRIP="${ac_tool_prefix}strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi STRIP=$ac_cv_prog_STRIP if test -n "$STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 $as_echo "$STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_STRIP"; then ac_ct_STRIP=$STRIP # Extract the first word of "strip", so it can be a program name with args. set dummy strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_STRIP"; then ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_STRIP="strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP if test -n "$ac_ct_STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 $as_echo "$ac_ct_STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_STRIP" = x; then STRIP="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac STRIP=$ac_ct_STRIP fi else STRIP="$ac_cv_prog_STRIP" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. set dummy ${ac_tool_prefix}ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_RANLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$RANLIB"; then ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi RANLIB=$ac_cv_prog_RANLIB if test -n "$RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 $as_echo "$RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_RANLIB"; then ac_ct_RANLIB=$RANLIB # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_RANLIB"; then ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_RANLIB="ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB if test -n "$ac_ct_RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 $as_echo "$ac_ct_RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_RANLIB" = x; then RANLIB="true" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac RANLIB=$ac_ct_RANLIB fi else RANLIB="$ac_cv_prog_RANLIB" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args. set dummy ${ac_tool_prefix}ar; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_AR+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$AR"; then ac_cv_prog_AR="$AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_AR="${ac_tool_prefix}ar" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AR=$ac_cv_prog_AR if test -n "$AR"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 $as_echo "$AR" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_AR"; then ac_ct_AR=$AR # Extract the first word of "ar", so it can be a program name with args. set dummy ar; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_AR+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_AR"; then ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_AR="ar" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_AR=$ac_cv_prog_ac_ct_AR if test -n "$ac_ct_AR"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 $as_echo "$ac_ct_AR" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_AR" = x; then AR="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac AR=$ac_ct_AR fi else AR="$ac_cv_prog_AR" fi ARFLAGS=rcu if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_PKG_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else case $PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi PKG_CONFIG=$ac_cv_path_PKG_CONFIG if test -n "$PKG_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 $as_echo "$PKG_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_path_PKG_CONFIG"; then ac_pt_PKG_CONFIG=$PKG_CONFIG # Extract the first word of "pkg-config", so it can be a program name with args. set dummy pkg-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else case $ac_pt_PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG if test -n "$ac_pt_PKG_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5 $as_echo "$ac_pt_PKG_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_pt_PKG_CONFIG" = x; then PKG_CONFIG="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac PKG_CONFIG=$ac_pt_PKG_CONFIG fi else PKG_CONFIG="$ac_cv_path_PKG_CONFIG" fi fi if test -n "$PKG_CONFIG"; then _pkg_min_version=0.9.0 { $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5 $as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; } if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } PKG_CONFIG="" fi fi for ac_prog in rst2man do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_RST2MAN+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$RST2MAN"; then ac_cv_prog_RST2MAN="$RST2MAN" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_RST2MAN="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi RST2MAN=$ac_cv_prog_RST2MAN if test -n "$RST2MAN"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RST2MAN" >&5 $as_echo "$RST2MAN" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$RST2MAN" && break done if test "$PORTNAME" = "win32"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}windres", so it can be a program name with args. set dummy ${ac_tool_prefix}windres; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_WINDRES+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$WINDRES"; then ac_cv_prog_WINDRES="$WINDRES" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_WINDRES="${ac_tool_prefix}windres" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi WINDRES=$ac_cv_prog_WINDRES if test -n "$WINDRES"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $WINDRES" >&5 $as_echo "$WINDRES" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_WINDRES"; then ac_ct_WINDRES=$WINDRES # Extract the first word of "windres", so it can be a program name with args. set dummy windres; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_WINDRES+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_WINDRES"; then ac_cv_prog_ac_ct_WINDRES="$ac_ct_WINDRES" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_WINDRES="windres" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_WINDRES=$ac_cv_prog_ac_ct_WINDRES if test -n "$ac_ct_WINDRES"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_WINDRES" >&5 $as_echo "$ac_ct_WINDRES" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_WINDRES" = x; then WINDRES="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac WINDRES=$ac_ct_WINDRES fi else WINDRES="$ac_cv_prog_WINDRES" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}dllwrap", so it can be a program name with args. set dummy ${ac_tool_prefix}dllwrap; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_DLLWRAP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$DLLWRAP"; then ac_cv_prog_DLLWRAP="$DLLWRAP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_DLLWRAP="${ac_tool_prefix}dllwrap" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi DLLWRAP=$ac_cv_prog_DLLWRAP if test -n "$DLLWRAP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DLLWRAP" >&5 $as_echo "$DLLWRAP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_DLLWRAP"; then ac_ct_DLLWRAP=$DLLWRAP # Extract the first word of "dllwrap", so it can be a program name with args. set dummy dllwrap; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_DLLWRAP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_DLLWRAP"; then ac_cv_prog_ac_ct_DLLWRAP="$ac_ct_DLLWRAP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_DLLWRAP="dllwrap" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_DLLWRAP=$ac_cv_prog_ac_ct_DLLWRAP if test -n "$ac_ct_DLLWRAP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DLLWRAP" >&5 $as_echo "$ac_ct_DLLWRAP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_DLLWRAP" = x; then DLLWRAP="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DLLWRAP=$ac_ct_DLLWRAP fi else DLLWRAP="$ac_cv_prog_DLLWRAP" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}dlltool", so it can be a program name with args. set dummy ${ac_tool_prefix}dlltool; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_DLLTOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$DLLTOOL"; then ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_DLLTOOL="${ac_tool_prefix}dlltool" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi DLLTOOL=$ac_cv_prog_DLLTOOL if test -n "$DLLTOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DLLTOOL" >&5 $as_echo "$DLLTOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_DLLTOOL"; then ac_ct_DLLTOOL=$DLLTOOL # Extract the first word of "dlltool", so it can be a program name with args. set dummy dlltool; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_DLLTOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_DLLTOOL"; then ac_cv_prog_ac_ct_DLLTOOL="$ac_ct_DLLTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_DLLTOOL="dlltool" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_DLLTOOL=$ac_cv_prog_ac_ct_DLLTOOL if test -n "$ac_ct_DLLTOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DLLTOOL" >&5 $as_echo "$ac_ct_DLLTOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_DLLTOOL" = x; then DLLTOOL="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DLLTOOL=$ac_ct_DLLTOOL fi else DLLTOOL="$ac_cv_prog_DLLTOOL" fi fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. set dummy ${ac_tool_prefix}strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$STRIP"; then ac_cv_prog_STRIP="$STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_STRIP="${ac_tool_prefix}strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi STRIP=$ac_cv_prog_STRIP if test -n "$STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 $as_echo "$STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_STRIP"; then ac_ct_STRIP=$STRIP # Extract the first word of "strip", so it can be a program name with args. set dummy strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_STRIP"; then ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_STRIP="strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP if test -n "$ac_ct_STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 $as_echo "$ac_ct_STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_STRIP" = x; then STRIP="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac STRIP=$ac_ct_STRIP fi else STRIP="$ac_cv_prog_STRIP" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 $as_echo_n "checking for ANSI C header files... " >&6; } if ${ac_cv_header_stdc+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_header_stdc=yes else ac_cv_header_stdc=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : : else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) return 2; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : else ac_cv_header_stdc=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 $as_echo "$ac_cv_header_stdc" >&6; } if test $ac_cv_header_stdc = yes; then $as_echo "#define STDC_HEADERS 1" >>confdefs.h fi # On IRIX 5.3, sys/types and inttypes.h are conflicting. for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ inttypes.h stdint.h unistd.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default " if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in inttypes.h stdbool.h unistd.h sys/time.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in sys/socket.h poll.h sys/poll.h sys/un.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in arpa/inet.h netinet/in.h netinet/tcp.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in sys/param.h sys/uio.h pwd.h grp.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in sys/wait.h sys/mman.h syslog.h netdb.h dlfcn.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in err.h pthread.h endian.h sys/endian.h byteswap.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in malloc.h regex.h getopt.h fnmatch.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in langinfo.h xlocale.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in ucred.h sys/ucred.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" " #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_PARAM_H #include #endif " if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in sys/resource.h sys/wait.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done { $as_echo "$as_me:${as_lineno-$LINENO}: checking for inline" >&5 $as_echo_n "checking for inline... " >&6; } if ${ac_cv_c_inline+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_c_inline=no for ac_kw in inline __inline__ __inline; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifndef __cplusplus typedef int foo_t; static $ac_kw foo_t static_foo () {return 0; } $ac_kw foo_t foo () {return 0; } #endif _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_c_inline=$ac_kw fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext test "$ac_cv_c_inline" != no && break done fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_inline" >&5 $as_echo "$ac_cv_c_inline" >&6; } case $ac_cv_c_inline in inline | yes) ;; *) case $ac_cv_c_inline in no) ac_val=;; *) ac_val=$ac_cv_c_inline;; esac cat >>confdefs.h <<_ACEOF #ifndef __cplusplus #define inline $ac_val #endif _ACEOF ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for C/C++ restrict keyword" >&5 $as_echo_n "checking for C/C++ restrict keyword... " >&6; } if ${ac_cv_c_restrict+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_c_restrict=no # The order here caters to the fact that C++ does not require restrict. for ac_kw in __restrict __restrict__ _Restrict restrict; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ typedef int * int_ptr; int foo (int_ptr $ac_kw ip) { return ip[0]; } int main () { int s[1]; int * $ac_kw t = s; t[0] = 0; return foo(t) ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_c_restrict=$ac_kw fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext test "$ac_cv_c_restrict" != no && break done fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_restrict" >&5 $as_echo "$ac_cv_c_restrict" >&6; } case $ac_cv_c_restrict in restrict) ;; no) $as_echo "#define restrict /**/" >>confdefs.h ;; *) cat >>confdefs.h <<_ACEOF #define restrict $ac_cv_c_restrict _ACEOF ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5 $as_echo_n "checking whether byte ordering is bigendian... " >&6; } if ${ac_cv_c_bigendian+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_c_bigendian=unknown # See if we're dealing with a universal compiler. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifndef __APPLE_CC__ not a universal capable compiler #endif typedef int dummy; _ACEOF if ac_fn_c_try_compile "$LINENO"; then : # Check for potential -arch flags. It is not universal unless # there are at least two -arch flags with different values. ac_arch= ac_prev= for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do if test -n "$ac_prev"; then case $ac_word in i?86 | x86_64 | ppc | ppc64) if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then ac_arch=$ac_word else ac_cv_c_bigendian=universal break fi ;; esac ac_prev= elif test "x$ac_word" = "x-arch"; then ac_prev=arch fi done fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_c_bigendian = unknown; then # See if sys/param.h defines the BYTE_ORDER macro. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { #if ! (defined BYTE_ORDER && defined BIG_ENDIAN \ && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \ && LITTLE_ENDIAN) bogus endian macros #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : # It does; now see whether it defined to BIG_ENDIAN or not. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { #if BYTE_ORDER != BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_c_bigendian=yes else ac_cv_c_bigendian=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test $ac_cv_c_bigendian = unknown; then # See if defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris). cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { #if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN) bogus endian macros #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : # It does; now see whether it defined to _BIG_ENDIAN or not. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { #ifndef _BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_c_bigendian=yes else ac_cv_c_bigendian=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test $ac_cv_c_bigendian = unknown; then # Compile a test program. if test "$cross_compiling" = yes; then : # Try to guess by grepping values from an object file. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ short int ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; short int ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; int use_ascii (int i) { return ascii_mm[i] + ascii_ii[i]; } short int ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; short int ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; int use_ebcdic (int i) { return ebcdic_mm[i] + ebcdic_ii[i]; } extern int foo; int main () { return use_ascii (foo) == use_ebcdic (foo); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then ac_cv_c_bigendian=yes fi if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then if test "$ac_cv_c_bigendian" = unknown; then ac_cv_c_bigendian=no else # finding both strings is unlikely to happen, but who knows? ac_cv_c_bigendian=unknown fi fi fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int main () { /* Are we little or big endian? From Harbison&Steele. */ union { long int l; char c[sizeof (long int)]; } u; u.l = 1; return u.c[sizeof (long int) - 1] == 1; ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : ac_cv_c_bigendian=no else ac_cv_c_bigendian=yes fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5 $as_echo "$ac_cv_c_bigendian" >&6; } case $ac_cv_c_bigendian in #( yes) $as_echo "#define WORDS_BIGENDIAN 1" >>confdefs.h ;; #( no) ;; #( universal) $as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h ;; #( *) as_fn_error $? "unknown endianness presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;; esac # Check whether --enable-largefile was given. if test "${enable_largefile+set}" = set; then : enableval=$enable_largefile; fi if test "$enable_largefile" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for special C compiler options needed for large files" >&5 $as_echo_n "checking for special C compiler options needed for large files... " >&6; } if ${ac_cv_sys_largefile_CC+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_sys_largefile_CC=no if test "$GCC" != yes; then ac_save_CC=$CC while :; do # IRIX 6.2 and later do not support large files by default, # so use the C compiler's -n32 option if that helps. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : break fi rm -f core conftest.err conftest.$ac_objext CC="$CC -n32" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_largefile_CC=' -n32'; break fi rm -f core conftest.err conftest.$ac_objext break done CC=$ac_save_CC rm -f conftest.$ac_ext fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_CC" >&5 $as_echo "$ac_cv_sys_largefile_CC" >&6; } if test "$ac_cv_sys_largefile_CC" != no; then CC=$CC$ac_cv_sys_largefile_CC fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _FILE_OFFSET_BITS value needed for large files" >&5 $as_echo_n "checking for _FILE_OFFSET_BITS value needed for large files... " >&6; } if ${ac_cv_sys_file_offset_bits+:} false; then : $as_echo_n "(cached) " >&6 else while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_file_offset_bits=no; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _FILE_OFFSET_BITS 64 #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_file_offset_bits=64; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_sys_file_offset_bits=unknown break done fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_file_offset_bits" >&5 $as_echo "$ac_cv_sys_file_offset_bits" >&6; } case $ac_cv_sys_file_offset_bits in #( no | unknown) ;; *) cat >>confdefs.h <<_ACEOF #define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits _ACEOF ;; esac rm -rf conftest* if test $ac_cv_sys_file_offset_bits = unknown; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _LARGE_FILES value needed for large files" >&5 $as_echo_n "checking for _LARGE_FILES value needed for large files... " >&6; } if ${ac_cv_sys_large_files+:} false; then : $as_echo_n "(cached) " >&6 else while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_large_files=no; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _LARGE_FILES 1 #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_large_files=1; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_sys_large_files=unknown break done fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_large_files" >&5 $as_echo "$ac_cv_sys_large_files" >&6; } case $ac_cv_sys_large_files in #( no | unknown) ;; *) cat >>confdefs.h <<_ACEOF #define _LARGE_FILES $ac_cv_sys_large_files _ACEOF ;; esac rm -rf conftest* fi fi ac_fn_c_check_type "$LINENO" "pid_t" "ac_cv_type_pid_t" "$ac_includes_default" if test "x$ac_cv_type_pid_t" = xyes; then : else cat >>confdefs.h <<_ACEOF #define pid_t int _ACEOF fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uid_t in sys/types.h" >&5 $as_echo_n "checking for uid_t in sys/types.h... " >&6; } if ${ac_cv_type_uid_t+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "uid_t" >/dev/null 2>&1; then : ac_cv_type_uid_t=yes else ac_cv_type_uid_t=no fi rm -f conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_uid_t" >&5 $as_echo "$ac_cv_type_uid_t" >&6; } if test $ac_cv_type_uid_t = no; then $as_echo "#define uid_t int" >>confdefs.h $as_echo "#define gid_t int" >>confdefs.h fi ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default" if test "x$ac_cv_type_size_t" = xyes; then : else cat >>confdefs.h <<_ACEOF #define size_t unsigned int _ACEOF fi ac_fn_c_find_uintX_t "$LINENO" "8" "ac_cv_c_uint8_t" case $ac_cv_c_uint8_t in #( no|yes) ;; #( *) $as_echo "#define _UINT8_T 1" >>confdefs.h cat >>confdefs.h <<_ACEOF #define uint8_t $ac_cv_c_uint8_t _ACEOF ;; esac ac_fn_c_find_uintX_t "$LINENO" "32" "ac_cv_c_uint32_t" case $ac_cv_c_uint32_t in #( no|yes) ;; #( *) $as_echo "#define _UINT32_T 1" >>confdefs.h cat >>confdefs.h <<_ACEOF #define uint32_t $ac_cv_c_uint32_t _ACEOF ;; esac ac_fn_c_find_uintX_t "$LINENO" "64" "ac_cv_c_uint64_t" case $ac_cv_c_uint64_t in #( no|yes) ;; #( *) $as_echo "#define _UINT64_T 1" >>confdefs.h cat >>confdefs.h <<_ACEOF #define uint64_t $ac_cv_c_uint64_t _ACEOF ;; esac ### Functions provided if missing for ac_func in strlcpy strlcat strsep getpeereid sigaction sigqueue do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done for ac_func in memmem memrchr mempcpy do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done for ac_func in inet_ntop inet_pton poll getline regcomp do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done for ac_func in err errx warn warnx getprogname setprogname do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done for ac_func in posix_memalign memalign valloc explicit_bzero memset_s reallocarray do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done for ac_func in getopt getopt_long getopt_long_only do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done for ac_func in fls flsl flsll ffs ffsl ffsll do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done for ac_func in fnmatch mbsnrtowcs nl_langinfo strtod_l strtonum do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done for ac_func in asprintf vasprintf timegm do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done ### Functions provided only on win32 for ac_func in localtime_r gettimeofday recvmsg sendmsg usleep getrusage do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done ### Functions used by libusual itself for ac_func in syslog mmap getpeerucred arc4random_buf getentropy do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done ### win32: link with ws2_32 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing WSAGetLastError" >&5 $as_echo_n "checking for library containing WSAGetLastError... " >&6; } if ${ac_cv_search_WSAGetLastError+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char WSAGetLastError (); int main () { return WSAGetLastError (); ; return 0; } _ACEOF for ac_lib in '' ws2_32; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_WSAGetLastError=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_WSAGetLastError+:} false; then : break fi done if ${ac_cv_search_WSAGetLastError+:} false; then : else ac_cv_search_WSAGetLastError=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_WSAGetLastError" >&5 $as_echo "$ac_cv_search_WSAGetLastError" >&6; } ac_res=$ac_cv_search_WSAGetLastError if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi ac_fn_c_check_decl "$LINENO" "strerror_r" "ac_cv_have_decl_strerror_r" "$ac_includes_default" if test "x$ac_cv_have_decl_strerror_r" = xyes; then : ac_have_decl=1 else ac_have_decl=0 fi cat >>confdefs.h <<_ACEOF #define HAVE_DECL_STRERROR_R $ac_have_decl _ACEOF for ac_func in strerror_r do : ac_fn_c_check_func "$LINENO" "strerror_r" "ac_cv_func_strerror_r" if test "x$ac_cv_func_strerror_r" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_STRERROR_R 1 _ACEOF fi done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether strerror_r returns char *" >&5 $as_echo_n "checking whether strerror_r returns char *... " >&6; } if ${ac_cv_func_strerror_r_char_p+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_func_strerror_r_char_p=no if test $ac_cv_have_decl_strerror_r = yes; then cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int main () { char buf[100]; char x = *strerror_r (0, buf, sizeof buf); char *p = strerror_r (0, buf, sizeof buf); return !p || x; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_func_strerror_r_char_p=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext else # strerror_r is not declared. Choose between # systems that have relatively inaccessible declarations for the # function. BeOS and DEC UNIX 4.0 fall in this category, but the # former has a strerror_r that returns char*, while the latter # has a strerror_r that returns `int'. # This test should segfault on the DEC system. if test "$cross_compiling" = yes; then : : else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default extern char *strerror_r (); int main () { char buf[100]; char x = *strerror_r (0, buf, sizeof buf); return ! isalpha (x); ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : ac_cv_func_strerror_r_char_p=yes fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_strerror_r_char_p" >&5 $as_echo "$ac_cv_func_strerror_r_char_p" >&6; } if test $ac_cv_func_strerror_r_char_p = yes; then $as_echo "#define STRERROR_R_CHAR_P 1" >>confdefs.h fi ### { $as_echo "$as_me:${as_lineno-$LINENO}: checking for integer enc/dec functions" >&5 $as_echo_n "checking for integer enc/dec functions... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #ifdef HAVE_SYS_ENDIAN_H #include #endif #ifdef HAVE_ENDIAN_H #include #endif char p[] = "01234567"; int main(void) { be16enc(p, 0); be32enc(p, 1); be64enc(p, 2); le16enc(p, 2); le32enc(p, 3); le64enc(p, 4); return (int)(be16dec(p) + be32dec(p) + be64dec(p)) + (int)(le16dec(p) + le32dec(p) + le64dec(p)); } _ACEOF if ac_fn_c_try_link "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: found" >&5 $as_echo "found" >&6; } $as_echo "#define HAVE_ENCDEC_FUNCS 1" >>confdefs.h else { $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5 $as_echo "not found" >&6; } fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing clock_gettime" >&5 $as_echo_n "checking for library containing clock_gettime... " >&6; } if ${ac_cv_search_clock_gettime+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char clock_gettime (); int main () { return clock_gettime (); ; return 0; } _ACEOF for ac_lib in '' rt; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_clock_gettime=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_clock_gettime+:} false; then : break fi done if ${ac_cv_search_clock_gettime+:} false; then : else ac_cv_search_clock_gettime=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_clock_gettime" >&5 $as_echo "$ac_cv_search_clock_gettime" >&6; } ac_res=$ac_cv_search_clock_gettime if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing getsockname" >&5 $as_echo_n "checking for library containing getsockname... " >&6; } if ${ac_cv_search_getsockname+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char getsockname (); int main () { return getsockname (); ; return 0; } _ACEOF for ac_lib in '' socket; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_getsockname=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_getsockname+:} false; then : break fi done if ${ac_cv_search_getsockname+:} false; then : else ac_cv_search_getsockname=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_getsockname" >&5 $as_echo "$ac_cv_search_getsockname" >&6; } ac_res=$ac_cv_search_getsockname if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing gethostbyname" >&5 $as_echo_n "checking for library containing gethostbyname... " >&6; } if ${ac_cv_search_gethostbyname+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char gethostbyname (); int main () { return gethostbyname (); ; return 0; } _ACEOF for ac_lib in '' nsl; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_gethostbyname=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_gethostbyname+:} false; then : break fi done if ${ac_cv_search_gethostbyname+:} false; then : else ac_cv_search_gethostbyname=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_gethostbyname" >&5 $as_echo "$ac_cv_search_gethostbyname" >&6; } ac_res=$ac_cv_search_gethostbyname if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing hstrerror" >&5 $as_echo_n "checking for library containing hstrerror... " >&6; } if ${ac_cv_search_hstrerror+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char hstrerror (); int main () { return hstrerror (); ; return 0; } _ACEOF for ac_lib in '' resolv; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_hstrerror=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_hstrerror+:} false; then : break fi done if ${ac_cv_search_hstrerror+:} false; then : else ac_cv_search_hstrerror=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_hstrerror" >&5 $as_echo "$ac_cv_search_hstrerror" >&6; } ac_res=$ac_cv_search_hstrerror if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi for ac_func in lstat do : ac_fn_c_check_func "$LINENO" "lstat" "ac_cv_func_lstat" if test "x$ac_cv_func_lstat" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LSTAT 1 _ACEOF fi done levent=yes { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libevent" >&5 $as_echo_n "checking for libevent... " >&6; } # Check whether --with-libevent was given. if test "${with_libevent+set}" = set; then : withval=$with_libevent; if test "$withval" = "no"; then levent=no elif test "$withval" = "yes"; then levent=yes else levent=yes CPPFLAGS="$CPPFLAGS -I$withval/include" LDFLAGS="$LDFLAGS -L$withval/lib" fi fi if test "$levent" = "no"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: using usual/event" >&5 $as_echo "using usual/event" >&6; } $as_echo "#define HAVE_EVENT_LOOPBREAK 1" >>confdefs.h $as_echo "#define HAVE_EVENT_BASE_NEW 1" >>confdefs.h have_libevent=no else # libevent $as_echo "#define HAVE_LIBEVENT 1" >>confdefs.h LIBS="-levent $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main(void) { struct event ev; event_init(); event_set(&ev, 1, EV_READ, NULL, NULL); /* this checks for 1.2+ */ event_base_free(NULL); } _ACEOF if ac_fn_c_try_link "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: found" >&5 $as_echo "found" >&6; } else as_fn_error $? "not found, cannot proceed" "$LINENO" 5 fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext for ac_func in event_loopbreak event_base_new evdns_base_new do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done have_libevent=yes fi # libevent ## ## DNS backend ## # make sure all vars are set use_cares=auto use_udns=no use_evdns=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use c-ares for DNS lookups" >&5 $as_echo_n "checking whether to use c-ares for DNS lookups... " >&6; } # Check whether --with-cares was given. if test "${with_cares+set}" = set; then : withval=$with_cares; if test "$withval" = "no"; then use_cares=no elif test "$withval" = "yes"; then use_cares=yes CARES_CFLAGS="" CARES_LIBS="-lcares" else use_cares=yes CARES_CFLAGS="-I$withval/include" CARES_LIBS="-L$withval/lib -lcares" fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $use_cares" >&5 $as_echo "$use_cares" >&6; } if test "$use_cares" = "auto"; then pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for CARES" >&5 $as_echo_n "checking for CARES... " >&6; } if test -n "$CARES_CFLAGS"; then pkg_cv_CARES_CFLAGS="$CARES_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libcares >= 1.6.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "libcares >= 1.6.0") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_CARES_CFLAGS=`$PKG_CONFIG --cflags "libcares >= 1.6.0" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$CARES_LIBS"; then pkg_cv_CARES_LIBS="$CARES_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libcares >= 1.6.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "libcares >= 1.6.0") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_CARES_LIBS=`$PKG_CONFIG --libs "libcares >= 1.6.0" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then CARES_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libcares >= 1.6.0" 2>&1` else CARES_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libcares >= 1.6.0" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$CARES_PKG_ERRORS" >&5 use_cares=no elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } use_cares=no else CARES_CFLAGS=$pkg_cv_CARES_CFLAGS CARES_LIBS=$pkg_cv_CARES_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } use_cares=yes fi fi if test "$use_cares" = "yes"; then $as_echo "#define USE_CARES 1" >>confdefs.h # does it support SOA parse tmp_CFLAGS="$CFLAGS" tmp_LIBS="$LIBS" CFLAGS="$CARES_CFLAGS $CFLAGS" LIBS="$CARES_LIBS $LIBS" for ac_func in ares_parse_soa_reply do : ac_fn_c_check_func "$LINENO" "ares_parse_soa_reply" "ac_cv_func_ares_parse_soa_reply" if test "x$ac_cv_func_ares_parse_soa_reply" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_ARES_PARSE_SOA_REPLY 1 _ACEOF fi done LIBS="$tmp_LIBS" CFLAGS="$tmp_CFLAGS" else { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use libudns" >&5 $as_echo_n "checking whether to use libudns... " >&6; } # Check whether --with-udns was given. if test "${with_udns+set}" = set; then : withval=$with_udns; if test "$withval" = "no"; then use_udns=no elif test "$withval" = "yes"; then use_udns=yes else use_udns=yes CPPFLAGS="$CPPFLAGS -I$withval/include" LDFLAGS="$LDFLAGS -L$withval/lib" fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $use_udns" >&5 $as_echo "$use_udns" >&6; } if test "$use_udns" = "yes"; then $as_echo "#define USE_UDNS 1" >>confdefs.h LIBS="-ludns $LIBS" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether libudns is available" >&5 $as_echo_n "checking whether libudns is available... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main(void) { struct dns_ctx *ctx = NULL; dns_init(ctx, 0); dns_reset(ctx); } _ACEOF if ac_fn_c_try_link "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: found" >&5 $as_echo "found" >&6; } else as_fn_error $? "not found, cannot proceed" "$LINENO" 5 fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext else # !udns if test "$ac_cv_func_evdns_base_new" = "yes"; then use_evdns=yes fi # Check whether --enable-evdns was given. if test "${enable_evdns+set}" = set; then : enableval=$enable_evdns; use_evdns=$enableval fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use libevent for DNS lookups" >&5 $as_echo_n "checking whether to use libevent for DNS lookups... " >&6; } if test "$use_evdns" = "yes"; then $as_echo "#define USE_EVDNS 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "$use_udns.$use_cares.$use_evdns" = "no.no.no"; then # Make sure we can run config.sub. $SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 $as_echo_n "checking build system type... " >&6; } if ${ac_cv_build+:} false; then : $as_echo_n "(cached) " >&6 else ac_build_alias=$build_alias test "x$ac_build_alias" = x && ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` test "x$ac_build_alias" = x && as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 $as_echo "$ac_cv_build" >&6; } case $ac_cv_build in *-*-*) ;; *) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; esac build=$ac_cv_build ac_save_IFS=$IFS; IFS='-' set x $ac_cv_build shift build_cpu=$1 build_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: build_os=$* IFS=$ac_save_IFS case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 $as_echo_n "checking host system type... " >&6; } if ${ac_cv_host+:} false; then : $as_echo_n "(cached) " >&6 else if test "x$host_alias" = x; then ac_cv_host=$ac_cv_build else ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 $as_echo "$ac_cv_host" >&6; } case $ac_cv_host in *-*-*) ;; *) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; esac host=$ac_cv_host ac_save_IFS=$IFS; IFS='-' set x $ac_cv_host shift host_cpu=$1 host_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: host_os=$* IFS=$ac_save_IFS case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing getaddrinfo_a" >&5 $as_echo_n "checking for library containing getaddrinfo_a... " >&6; } if ${ac_cv_search_getaddrinfo_a+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char getaddrinfo_a (); int main () { return getaddrinfo_a (); ; return 0; } _ACEOF for ac_lib in '' anl; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_getaddrinfo_a=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_getaddrinfo_a+:} false; then : break fi done if ${ac_cv_search_getaddrinfo_a+:} false; then : else ac_cv_search_getaddrinfo_a=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_getaddrinfo_a" >&5 $as_echo "$ac_cv_search_getaddrinfo_a" >&6; } ac_res=$ac_cv_search_getaddrinfo_a if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use native getaddinfo_a" >&5 $as_echo_n "checking whether to use native getaddinfo_a... " >&6; } if ${ac_cv_usual_glibc_gaia+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #ifdef HAVE_NETDB_H #include #endif int main () { #if __GLIBC_PREREQ(2,9) getaddrinfo_a(0,NULL,0,NULL); #else none or broken #endif ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_usual_glibc_gaia=yes else ac_cv_usual_glibc_gaia=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_usual_glibc_gaia" >&5 $as_echo "$ac_cv_usual_glibc_gaia" >&6; } if test x"$ac_cv_usual_glibc_gaia" = xyes ; then $as_echo "#define HAVE_GETADDRINFO_A 1" >>confdefs.h else ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu acx_pthread_ok=no # We used to check for pthread.h first, but this fails if pthread.h # requires special compiler flags (e.g. on True64 or Sequent). # It gets checked for in the link test anyway. # First of all, check if the user has set any of the PTHREAD_LIBS, # etcetera environment variables, and if threads linking works using # them: if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS" >&5 $as_echo_n "checking for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char pthread_join (); int main () { return pthread_join (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : acx_pthread_ok=yes fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $acx_pthread_ok" >&5 $as_echo "$acx_pthread_ok" >&6; } if test x"$acx_pthread_ok" = xno; then PTHREAD_LIBS="" PTHREAD_CFLAGS="" fi LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" fi # We must check for the threads library under a number of different # names; the ordering is very important because some systems # (e.g. DEC) have both -lpthread and -lpthreads, where one of the # libraries is broken (non-POSIX). # Create a list of thread flags to try. Items starting with a "-" are # C compiler flags, and other items are library names, except for "none" # which indicates that we try without any flags at all, and "pthread-config" # which is a program returning the flags for the Pth emulation library. acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" # The ordering *is* (sometimes) important. Some notes on the # individual items follow: # pthreads: AIX (must check this before -lpthread) # none: in case threads are in libc; should be tried before -Kthread and # other compiler flags to prevent continual compiler warnings # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) # -pthreads: Solaris/gcc # -mthreads: Mingw32/gcc, Lynx/gcc # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it # doesn't hurt to check since this sometimes defines pthreads too; # also defines -D_REENTRANT) # ... -mt is also the pthreads flag for HP/aCC # pthread: Linux, etcetera # --thread-safe: KAI C++ # pthread-config: use pthread-config program (for GNU Pth library) case "${host_cpu}-${host_os}" in *solaris*) # On Solaris (at least, for some versions), libc contains stubbed # (non-functional) versions of the pthreads routines, so link-based # tests will erroneously succeed. (We need to link with -pthreads/-mt/ # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather # a function called by this macro, so we could check for that, but # who knows whether they'll stub that too in a future libc.) So, # we'll just look for -pthreads and -lpthread first: acx_pthread_flags="-pthreads pthread -mt -pthread $acx_pthread_flags" ;; esac if test x"$acx_pthread_ok" = xno; then for flag in $acx_pthread_flags; do case $flag in none) { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthreads work without any flags" >&5 $as_echo_n "checking whether pthreads work without any flags... " >&6; } ;; -*) { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthreads work with $flag" >&5 $as_echo_n "checking whether pthreads work with $flag... " >&6; } PTHREAD_CFLAGS="$flag" ;; pthread-config) # Extract the first word of "pthread-config", so it can be a program name with args. set dummy pthread-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_acx_pthread_config+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$acx_pthread_config"; then ac_cv_prog_acx_pthread_config="$acx_pthread_config" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_acx_pthread_config="yes" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_prog_acx_pthread_config" && ac_cv_prog_acx_pthread_config="no" fi fi acx_pthread_config=$ac_cv_prog_acx_pthread_config if test -n "$acx_pthread_config"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $acx_pthread_config" >&5 $as_echo "$acx_pthread_config" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test x"$acx_pthread_config" = xno; then continue; fi PTHREAD_CFLAGS="`pthread-config --cflags`" PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" ;; *) { $as_echo "$as_me:${as_lineno-$LINENO}: checking for the pthreads library -l$flag" >&5 $as_echo_n "checking for the pthreads library -l$flag... " >&6; } PTHREAD_LIBS="-l$flag" ;; esac save_LIBS="$LIBS" save_CFLAGS="$CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # Check for various functions. We must include pthread.h, # since some functions may be macros. (On the Sequent, we # need a special flag -Kthread to make this header compile.) # We check for pthread_join because it is in -lpthread on IRIX # while pthread_create is in libc. We check for pthread_attr_init # due to DEC craziness with -lpthreads. We check for # pthread_cleanup_push because it is one of the few pthread # functions on Solaris that doesn't have a non-functional libc stub. # We try pthread_create on general principles. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include <pthread.h> int main () { pthread_t th; pthread_join(th, 0); pthread_attr_init(0); pthread_cleanup_push(0, 0); pthread_create(0,0,0,0); pthread_cleanup_pop(0); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : acx_pthread_ok=yes fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $acx_pthread_ok" >&5 $as_echo "$acx_pthread_ok" >&6; } if test "x$acx_pthread_ok" = xyes; then break; fi PTHREAD_LIBS="" PTHREAD_CFLAGS="" done fi # Various other checks: if test "x$acx_pthread_ok" = xyes; then save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for joinable pthread attribute" >&5 $as_echo_n "checking for joinable pthread attribute... " >&6; } attr_name=unknown for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include <pthread.h> int main () { int attr=$attr; return attr; ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : attr_name=$attr; break fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext done { $as_echo "$as_me:${as_lineno-$LINENO}: result: $attr_name" >&5 $as_echo "$attr_name" >&6; } if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then cat >>confdefs.h <<_ACEOF #define PTHREAD_CREATE_JOINABLE $attr_name _ACEOF fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking if more special flags are required for pthreads" >&5 $as_echo_n "checking if more special flags are required for pthreads... " >&6; } flag=no case "${host_cpu}-${host_os}" in *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";; *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${flag}" >&5 $as_echo "${flag}" >&6; } if test "x$flag" != xno; then PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" fi LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" # More AIX lossage: must compile with xlc_r or cc_r if test x"$GCC" != xyes; then for ac_prog in xlc_r cc_r do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_PTHREAD_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$PTHREAD_CC"; then ac_cv_prog_PTHREAD_CC="$PTHREAD_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_PTHREAD_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi PTHREAD_CC=$ac_cv_prog_PTHREAD_CC if test -n "$PTHREAD_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PTHREAD_CC" >&5 $as_echo "$PTHREAD_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$PTHREAD_CC" && break done test -n "$PTHREAD_CC" || PTHREAD_CC="${CC}" else PTHREAD_CC=$CC fi else PTHREAD_CC="$CC" fi # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: if test x"$acx_pthread_ok" = xyes; then $as_echo "#define HAVE_PTHREAD 1" >>confdefs.h : else acx_pthread_ok=no { $as_echo "$as_me:${as_lineno-$LINENO}: result: Threads not available and fallback getaddrinfo_a() non-functional." >&5 $as_echo "Threads not available and fallback getaddrinfo_a() non-functional." >&6; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu CC="$PTHREAD_CC" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LIBS="$LIBS $PTHREAD_LIBS" fi fi fi # !udns fi # !cares ## end of DNS tls_support=auto TLS_CPPFLAGS="" TLS_LDFLAGS="" TLS_LIBS="" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for OpenSSL" >&5 $as_echo_n "checking for OpenSSL... " >&6; } # Check whether --with-openssl was given. if test "${with_openssl+set}" = set; then : withval=$with_openssl; if test "$withval" = "no"; then tls_support=no elif test "$withval" = "yes"; then tls_support=libssl TLS_LIBS="-lssl -lcrypto" else tls_support=libssl TLS_CPPFLAGS="-I$withval/include" TLS_LDFLAGS="-L$withval/lib" TLS_LIBS="-lssl -lcrypto" fi else tls_support=auto TLS_CPPFLAGS="" TLS_LDFLAGS="" TLS_LIBS="-lssl -lcrypto" fi if test "$tls_support" = "auto" -o "$tls_support" = "libssl"; then $as_echo "#define USUAL_LIBSSL_FOR_TLS 1" >>confdefs.h tmp_LIBS="$LIBS" tmp_LDFLAGS="$LDFLAGS" tmp_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$TLS_CPPFLAGS $CPPFLAGS" LDFLAGS="$TLS_LDFLAGS $LDFLAGS" LIBS="$TLS_LIBS $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { SSL_CTX *ctx = SSL_CTX_new(TLSv1_2_method()); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : tls_support=yes; { $as_echo "$as_me:${as_lineno-$LINENO}: result: found" >&5 $as_echo "found" >&6; } else as_fn_error $? "not found" "$LINENO" 5 fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext for ac_func in SSL_CTX_use_certificate_chain_mem SSL_CTX_load_verify_mem asn1_time_parse do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done CPPFLAGS="$tmp_CPPFLAGS" LDFLAGS="$tmp_LDFLAGS" LIBS="$tmp_LIBS" cafile=auto { $as_echo "$as_me:${as_lineno-$LINENO}: checking for root CA certs" >&5 $as_echo_n "checking for root CA certs... " >&6; } # Check whether --with-root-ca-file was given. if test "${with_root_ca_file+set}" = set; then : withval=$with_root_ca_file; if test "$withval" = "no"; then : elif test "$withval" = "yes"; then : else cafile="$withval" fi fi if test "$cafile" = "auto"; then for cafile in /etc/ssl/certs/ca-certificates.crt /etc/ssl/cert.pem; do if test -f "$cafile"; then break fi done fi cat >>confdefs.h <<_ACEOF #define USUAL_TLS_CA_FILE "$cafile" _ACEOF { $as_echo "$as_me:${as_lineno-$LINENO}: result: $cafile" >&5 $as_echo "$cafile" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi # Check whether --enable-debug was given. if test "${enable_debug+set}" = set; then : enableval=$enable_debug; else enable_debug=yes fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build debug binary" >&5 $as_echo_n "checking whether to build debug binary... " >&6; } if test "$enable_debug" = "yes"; then LDFLAGS="-g $LDFLAGS" BININSTALL="$INSTALL" { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else BININSTALL="$INSTALL -s" { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi # Check whether --enable-cassert was given. if test "${enable_cassert+set}" = set; then : enableval=$enable_cassert; fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable asserts" >&5 $as_echo_n "checking whether to enable asserts... " >&6; } if test "$enable_cassert" = "yes"; then $as_echo "#define CASSERT 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi # Check whether --enable-werror was given. if test "${enable_werror+set}" = set; then : enableval=$enable_werror; fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to fail on warnings" >&5 $as_echo_n "checking whether to fail on warnings... " >&6; } if test "$enable_werror" = "yes"; then CFLAGS="$CFLAGS -Werror" { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi ac_config_files="$ac_config_files config.mak" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 $as_echo "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 $as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`$as_echo "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 $as_echo "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -p'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -p' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -p' fi else as_ln_s='cp -p' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi if test -x / >/dev/null 2>&1; then as_test_x='test -x' else if ls -dL / >/dev/null 2>&1; then as_ls_L_option=L else as_ls_L_option= fi as_test_x=' eval sh -c '\'' if test -d "$1"; then test -d "$1/."; else case $1 in #( -*)set "./$1";; esac; case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( ???[sx]*):;;*)false;;esac;fi '\'' sh ' fi as_executable_p=$as_test_x # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by pgbouncer $as_me 1.7, which was generated by GNU Autoconf 2.68. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac case $ac_config_headers in *" "*) set x $ac_config_headers; shift; ac_config_headers=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" config_headers="$ac_config_headers" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Report bugs to the package provider." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ pgbouncer config.status 1.7 configured by $0, generated by GNU Autoconf 2.68, with options \\"\$ac_cs_config\\" Copyright (C) 2010 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' INSTALL='$INSTALL' MKDIR_P='$MKDIR_P' AWK='$AWK' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) $as_echo "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) $as_echo "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append CONFIG_HEADERS " '$ac_optarg'" ac_need_defaults=false;; --he | --h) # Conflict between --help and --header as_fn_error $? "ambiguous option: \`$1' Try \`$0 --help' for more information.";; --help | --hel | -h ) $as_echo "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX $as_echo "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "lib/usual/config.h") CONFIG_HEADERS="$CONFIG_HEADERS lib/usual/config.h" ;; "config.mak") CONFIG_FILES="$CONFIG_FILES config.mak" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" # Set up the scripts for CONFIG_HEADERS section. # No need to generate them if there are no CONFIG_HEADERS. # This happens for instance with `./config.status Makefile'. if test -n "$CONFIG_HEADERS"; then cat >"$ac_tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF # Transform confdefs.h into an awk script `defines.awk', embedded as # here-document in config.status, that substitutes the proper values into # config.h.in to produce config.h. # Create a delimiter string that does not exist in confdefs.h, to ease # handling of long lines. ac_delim='%!_!# ' for ac_last_try in false false :; do ac_tt=`sed -n "/$ac_delim/p" confdefs.h` if test -z "$ac_tt"; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done # For the awk script, D is an array of macro values keyed by name, # likewise P contains macro parameters if any. Preserve backslash # newline sequences. ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* sed -n ' s/.\{148\}/&'"$ac_delim"'/g t rset :rset s/^[ ]*#[ ]*define[ ][ ]*/ / t def d :def s/\\$// t bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3"/p s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p d :bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3\\\\\\n"\\/p t cont s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p t cont d :cont n s/.\{148\}/&'"$ac_delim"'/g t clear :clear s/\\$// t bsnlc s/["\\]/\\&/g; s/^/"/; s/$/"/p d :bsnlc s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p b cont ' >$CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 for (key in D) D_is_set[key] = 1 FS = "" } /^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { line = \$ 0 split(line, arg, " ") if (arg[1] == "#") { defundef = arg[2] mac1 = arg[3] } else { defundef = substr(arg[1], 2) mac1 = arg[2] } split(mac1, mac2, "(") #) macro = mac2[1] prefix = substr(line, 1, index(line, defundef) - 1) if (D_is_set[macro]) { # Preserve the white space surrounding the "#". print prefix "define", macro P[macro] D[macro] next } else { # Replace #undef with comments. This is necessary, for example, # in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. if (defundef == "undef") { print "/*", prefix defundef, macro, "*/" next } } } { print } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 $as_echo "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`$as_echo "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # case $INSTALL in [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; esac ac_MKDIR_P=$MKDIR_P case $MKDIR_P in [\\/$]* | ?:[\\/]* ) ;; */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; esac _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 $as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t s&@INSTALL@&$ac_INSTALL&;t t s&@MKDIR_P@&$ac_MKDIR_P&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :H) # # CONFIG_HEADER # if test x"$ac_file" != x-; then { $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" } >"$ac_tmp/config.h" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 $as_echo "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" mv "$ac_tmp/config.h" "$ac_file" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ || as_fn_error $? "could not create -" "$LINENO" 5 fi ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi test -f Makefile || { echo "Linking Makefile" ln -s $srcdir/Makefile } echo "" echo "Results" echo " c-ares = $use_cares" echo " evdns = $use_evdns" echo " udns = $use_udns" echo " tls = $tls_support" echo "" pgbouncer-1.7/config.guess0000755000175000017500000012743212635051441012620 00000000000000#! /bin/sh # Attempt to guess a canonical system name. # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, # 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, # 2011, 2012 Free Software Foundation, Inc. timestamp='2012-02-10' # This file 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. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Originally written by Per Bothner. Please send patches (context # diff format) to and include a ChangeLog # entry. # # This script attempts to guess a canonical system name similar to # config.sub. If it succeeds, it prints the system name on stdout, and # exits with 0. Otherwise, it exits with 1. # # You can get the latest version of this script from: # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; * ) break ;; esac done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi trap 'exit 1' 1 2 15 # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. # Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still # use `HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. set_cc_for_build=' trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; : ${TMPDIR=/tmp} ; { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; dummy=$tmp/dummy ; tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; case $CC_FOR_BUILD,$HOST_CC,$CC in ,,) echo "int x;" > $dummy.c ; for c in cc gcc c89 c99 ; do if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then CC_FOR_BUILD="$c"; break ; fi ; done ; if test x"$CC_FOR_BUILD" = x ; then CC_FOR_BUILD=no_compiler_found ; fi ;; ,,*) CC_FOR_BUILD=$CC ;; ,*,*) CC_FOR_BUILD=$HOST_CC ;; esac ; set_cc_for_build= ;' # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) if (test -f /.attbin/uname) >/dev/null 2>&1 ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown # Note: order is significant - the case branches are not exclusive. case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently # switched to ELF, *-*-netbsd* would select the old # object file format. This provides both forward # compatibility and a consistent mechanism for selecting the # object file format. # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". sysctl="sysctl -n hw.machine_arch" UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ /usr/sbin/$sysctl 2>/dev/null || echo unknown)` case "${UNAME_MACHINE_ARCH}" in armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; *) machine=${UNAME_MACHINE_ARCH}-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently, or will in the future. case "${UNAME_MACHINE_ARCH}" in arm*|i386|m68k|ns32k|sh3*|sparc|vax) eval $set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) os=netbsd ;; esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. case "${UNAME_VERSION}" in Debian*) release='-gnu' ;; *) release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. echo "${machine}-${os}${release}" exit ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} exit ;; *:ekkoBSD:*:*) echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} exit ;; *:SolidBSD:*:*) echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} exit ;; macppc:MirBSD:*:*) echo powerpc-unknown-mirbsd${UNAME_RELEASE} exit ;; *:MirBSD:*:*) echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} exit ;; alpha:OSF1:*:*) case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` ;; *5.*) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case "$ALPHA_CPU_TYPE" in "EV4 (21064)") UNAME_MACHINE="alpha" ;; "EV4.5 (21064)") UNAME_MACHINE="alpha" ;; "LCA4 (21066/21068)") UNAME_MACHINE="alpha" ;; "EV5 (21164)") UNAME_MACHINE="alphaev5" ;; "EV5.6 (21164A)") UNAME_MACHINE="alphaev56" ;; "EV5.6 (21164PC)") UNAME_MACHINE="alphapca56" ;; "EV5.7 (21164PC)") UNAME_MACHINE="alphapca57" ;; "EV6 (21264)") UNAME_MACHINE="alphaev6" ;; "EV6.7 (21264A)") UNAME_MACHINE="alphaev67" ;; "EV6.8CB (21264C)") UNAME_MACHINE="alphaev68" ;; "EV6.8AL (21264B)") UNAME_MACHINE="alphaev68" ;; "EV6.8CX (21264D)") UNAME_MACHINE="alphaev68" ;; "EV6.9A (21264/EV69A)") UNAME_MACHINE="alphaev69" ;; "EV7 (21364)") UNAME_MACHINE="alphaev7" ;; "EV7.9 (21364A)") UNAME_MACHINE="alphaev79" ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` # Reset EXIT trap before exiting to avoid spurious non-zero exit code. exitcode=$? trap '' 0 exit $exitcode ;; Alpha\ *:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # Should we change UNAME_MACHINE based on the output of uname instead # of the specific Alpha model? echo alpha-pc-interix exit ;; 21064:Windows_NT:50:3) echo alpha-dec-winnt3.5 exit ;; Amiga*:UNIX_System_V:4.0:*) echo m68k-unknown-sysv4 exit ;; *:[Aa]miga[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-amigaos exit ;; *:[Mm]orph[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-morphos exit ;; *:OS/390:*:*) echo i370-ibm-openedition exit ;; *:z/VM:*:*) echo s390-ibm-zvmoe exit ;; *:OS400:*:*) echo powerpc-ibm-os400 exit ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) echo arm-acorn-riscix${UNAME_RELEASE} exit ;; arm:riscos:*:*|arm:RISCOS:*:*) echo arm-unknown-riscos exit ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) echo hppa1.1-hitachi-hiuxmpp exit ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. if test "`(/bin/universe) 2>/dev/null`" = att ; then echo pyramid-pyramid-sysv3 else echo pyramid-pyramid-bsd fi exit ;; NILE*:*:*:dcosx) echo pyramid-pyramid-svr4 exit ;; DRS?6000:unix:4.0:6*) echo sparc-icl-nx6 exit ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in sparc) echo sparc-icl-nx7; exit ;; esac ;; s390x:SunOS:*:*) echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4H:SunOS:5.*:*) echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) echo i386-pc-auroraux${UNAME_RELEASE} exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) eval $set_cc_for_build SUN_ARCH="i386" # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then SUN_ARCH="x86_64" fi fi echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:*:*) case "`/usr/bin/arch -k`" in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` exit ;; sun3*:SunOS:*:*) echo m68k-sun-sunos${UNAME_RELEASE} exit ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 case "`/bin/arch`" in sun3) echo m68k-sun-sunos${UNAME_RELEASE} ;; sun4) echo sparc-sun-sunos${UNAME_RELEASE} ;; esac exit ;; aushp:SunOS:*:*) echo sparc-auspex-sunos${UNAME_RELEASE} exit ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor # > m68000). The system name ranges from "MiNT" over "FreeMiNT" # to the lowercase version "mint" (or "freemint"). Finally # the system name "TOS" denotes a system which is actually not # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) echo m68k-milan-mint${UNAME_RELEASE} exit ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) echo m68k-hades-mint${UNAME_RELEASE} exit ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) echo m68k-unknown-mint${UNAME_RELEASE} exit ;; m68k:machten:*:*) echo m68k-apple-machten${UNAME_RELEASE} exit ;; powerpc:machten:*:*) echo powerpc-apple-machten${UNAME_RELEASE} exit ;; RISC*:Mach:*:*) echo mips-dec-mach_bsd4.3 exit ;; RISC*:ULTRIX:*:*) echo mips-dec-ultrix${UNAME_RELEASE} exit ;; VAX*:ULTRIX*:*:*) echo vax-dec-ultrix${UNAME_RELEASE} exit ;; 2020:CLIX:*:* | 2430:CLIX:*:*) echo clipper-intergraph-clix${UNAME_RELEASE} exit ;; mips:*:*:UMIPS | mips:*:*:RISCos) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { #else int main (argc, argv) int argc; char *argv[]; { #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && SYSTEM_NAME=`$dummy $dummyarg` && { echo "$SYSTEM_NAME"; exit; } echo mips-mips-riscos${UNAME_RELEASE} exit ;; Motorola:PowerMAX_OS:*:*) echo powerpc-motorola-powermax exit ;; Motorola:*:4.3:PL8-*) echo powerpc-harris-powermax exit ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) echo powerpc-harris-powermax exit ;; Night_Hawk:Power_UNIX:*:*) echo powerpc-harris-powerunix exit ;; m88k:CX/UX:7*:*) echo m88k-harris-cxux7 exit ;; m88k:*:4*:R4*) echo m88k-motorola-sysv4 exit ;; m88k:*:3*:R3*) echo m88k-motorola-sysv3 exit ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] then if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ [ ${TARGET_BINARY_INTERFACE}x = x ] then echo m88k-dg-dgux${UNAME_RELEASE} else echo m88k-dg-dguxbcs${UNAME_RELEASE} fi else echo i586-dg-dgux${UNAME_RELEASE} fi exit ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) echo m88k-dolphin-sysv3 exit ;; M88*:*:R3*:*) # Delta 88k system running SVR3 echo m88k-motorola-sysv3 exit ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) echo m88k-tektronix-sysv3 exit ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) echo m68k-tektronix-bsd exit ;; *:IRIX*:*:*) echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` exit ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) echo i386-ibm-aix exit ;; ia64:AIX:*:*) if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} exit ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include main() { if (!__power_pc()) exit(1); puts("powerpc-ibm-aix3.2.5"); exit(0); } EOF if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` then echo "$SYSTEM_NAME" else echo rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then echo rs6000-ibm-aix3.2.4 else echo rs6000-ibm-aix3.2 fi exit ;; *:AIX:*:[4567]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${IBM_ARCH}-ibm-aix${IBM_REV} exit ;; *:AIX:*:*) echo rs6000-ibm-aix exit ;; ibmrt:4.4BSD:*|romp-ibm:BSD:*) echo romp-ibm-bsd4.4 exit ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to exit ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) echo rs6000-bull-bosx exit ;; DPX/2?00:B.O.S.:*:*) echo m68k-bull-sysv3 exit ;; 9000/[34]??:4.3bsd:1.*:*) echo m68k-hp-bsd exit ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) echo m68k-hp-bsd4.4 exit ;; 9000/[34678]??:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` case "${UNAME_MACHINE}" in 9000/31? ) HP_ARCH=m68000 ;; 9000/[34]?? ) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if [ -x /usr/bin/getconf ]; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case "${sc_cpu_version}" in 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case "${sc_kernel_bits}" in 32) HP_ARCH="hppa2.0n" ;; 64) HP_ARCH="hppa2.0w" ;; '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 esac ;; esac fi if [ "${HP_ARCH}" = "" ]; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #define _HPUX_SOURCE #include #include int main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); #endif long cpu = sysconf (_SC_CPU_VERSION); switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0"); break; case CPU_PA_RISC1_1: puts ("hppa1.1"); break; case CPU_PA_RISC2_0: #if defined(_SC_KERNEL_BITS) switch (bits) { case 64: puts ("hppa2.0w"); break; case 32: puts ("hppa2.0n"); break; default: puts ("hppa2.0"); break; } break; #else /* !defined(_SC_KERNEL_BITS) */ puts ("hppa2.0"); break; #endif default: puts ("hppa1.0"); break; } exit (0); } EOF (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac if [ ${HP_ARCH} = "hppa2.0w" ] then eval $set_cc_for_build # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler # generating 64-bit code. GNU and HP use different nomenclature: # # $ CC_FOR_BUILD=cc ./config.guess # => hppa2.0w-hp-hpux11.23 # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then HP_ARCH="hppa2.0w" else HP_ARCH="hppa64" fi fi echo ${HP_ARCH}-hp-hpux${HPUX_REV} exit ;; ia64:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` echo ia64-hp-hpux${HPUX_REV} exit ;; 3050*:HI-UX:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include int main () { long cpu = sysconf (_SC_CPU_VERSION); /* The order matters, because CPU_IS_HP_MC68K erroneously returns true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct results, however. */ if (CPU_IS_PA_RISC (cpu)) { switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; default: puts ("hppa-hitachi-hiuxwe2"); break; } } else if (CPU_IS_HP_MC68K (cpu)) puts ("m68k-hitachi-hiuxwe2"); else puts ("unknown-hitachi-hiuxwe2"); exit (0); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && { echo "$SYSTEM_NAME"; exit; } echo unknown-hitachi-hiuxwe2 exit ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) echo hppa1.1-hp-bsd exit ;; 9000/8??:4.3bsd:*:*) echo hppa1.0-hp-bsd exit ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) echo hppa1.0-hp-mpeix exit ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) echo hppa1.1-hp-osf exit ;; hp8??:OSF1:*:*) echo hppa1.0-hp-osf exit ;; i*86:OSF1:*:*) if [ -x /usr/sbin/sysversion ] ; then echo ${UNAME_MACHINE}-unknown-osf1mk else echo ${UNAME_MACHINE}-unknown-osf1 fi exit ;; parisc*:Lites*:*:*) echo hppa1.1-hp-lites exit ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) echo c1-convex-bsd exit ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) echo c34-convex-bsd exit ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) echo c38-convex-bsd exit ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) echo c4-convex-bsd exit ;; CRAY*Y-MP:*:*:*) echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*[A-Z]90:*:*:*) echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*T3E:*:*:*) echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*SV1:*:*:*) echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; *:UNICOS/mp:*:*) echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; 5000:UNIX_System_V:4.*:*) FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} exit ;; sparc*:BSD/OS:*:*) echo sparc-unknown-bsdi${UNAME_RELEASE} exit ;; *:BSD/OS:*:*) echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} exit ;; *:FreeBSD:*:*) UNAME_PROCESSOR=`/usr/bin/uname -p` case ${UNAME_PROCESSOR} in amd64) echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; *) echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; esac exit ;; i*:CYGWIN*:*) echo ${UNAME_MACHINE}-pc-cygwin exit ;; *:MINGW*:*) echo ${UNAME_MACHINE}-pc-mingw32 exit ;; i*:MSYS*:*) echo ${UNAME_MACHINE}-pc-msys exit ;; i*:windows32*:*) # uname -m includes "-pc" on this system. echo ${UNAME_MACHINE}-mingw32 exit ;; i*:PW*:*) echo ${UNAME_MACHINE}-pc-pw32 exit ;; *:Interix*:*) case ${UNAME_MACHINE} in x86) echo i586-pc-interix${UNAME_RELEASE} exit ;; authenticamd | genuineintel | EM64T) echo x86_64-unknown-interix${UNAME_RELEASE} exit ;; IA64) echo ia64-unknown-interix${UNAME_RELEASE} exit ;; esac ;; [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) echo i${UNAME_MACHINE}-pc-mks exit ;; 8664:Windows_NT:*) echo x86_64-pc-mks exit ;; i*:Windows_NT*:* | Pentium*:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we # UNAME_MACHINE based on the output of uname instead of i386? echo i586-pc-interix exit ;; i*:UWIN*:*) echo ${UNAME_MACHINE}-pc-uwin exit ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) echo x86_64-unknown-cygwin exit ;; p*:CYGWIN*:*) echo powerpcle-unknown-cygwin exit ;; prep*:SunOS:5.*:*) echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; *:GNU:*:*) # the GNU system echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu exit ;; i*86:Minix:*:*) echo ${UNAME_MACHINE}-pc-minix exit ;; aarch64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; PCA57) UNAME_MACHINE=alphapca56 ;; EV6) UNAME_MACHINE=alphaev6 ;; EV67) UNAME_MACHINE=alphaev67 ;; EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} exit ;; arm*:Linux:*:*) eval $set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then echo ${UNAME_MACHINE}-unknown-linux-gnu else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then echo ${UNAME_MACHINE}-unknown-linux-gnueabi else echo ${UNAME_MACHINE}-unknown-linux-gnueabihf fi fi exit ;; avr32*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; cris:Linux:*:*) echo ${UNAME_MACHINE}-axis-linux-gnu exit ;; crisv32:Linux:*:*) echo ${UNAME_MACHINE}-axis-linux-gnu exit ;; frv:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; hexagon:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; i*86:Linux:*:*) LIBC=gnu eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #ifdef __dietlibc__ LIBC=dietlibc #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'` echo "${UNAME_MACHINE}-pc-linux-${LIBC}" exit ;; ia64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; m32r*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; m68*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; mips:Linux:*:* | mips64:Linux:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #undef CPU #undef ${UNAME_MACHINE} #undef ${UNAME_MACHINE}el #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) CPU=${UNAME_MACHINE}el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) CPU=${UNAME_MACHINE} #else CPU= #endif #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } ;; or32:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; padre:Linux:*:*) echo sparc-unknown-linux-gnu exit ;; parisc64:Linux:*:* | hppa64:Linux:*:*) echo hppa64-unknown-linux-gnu exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in PA7*) echo hppa1.1-unknown-linux-gnu ;; PA8*) echo hppa2.0-unknown-linux-gnu ;; *) echo hppa-unknown-linux-gnu ;; esac exit ;; ppc64:Linux:*:*) echo powerpc64-unknown-linux-gnu exit ;; ppc:Linux:*:*) echo powerpc-unknown-linux-gnu exit ;; s390:Linux:*:* | s390x:Linux:*:*) echo ${UNAME_MACHINE}-ibm-linux exit ;; sh64*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; sh*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; sparc:Linux:*:* | sparc64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; tile*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; vax:Linux:*:*) echo ${UNAME_MACHINE}-dec-linux-gnu exit ;; x86_64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; xtensa*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. echo i386-sequent-sysv4 exit ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} exit ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. echo ${UNAME_MACHINE}-pc-os2-emx exit ;; i*86:XTS-300:*:STOP) echo ${UNAME_MACHINE}-unknown-stop exit ;; i*86:atheos:*:*) echo ${UNAME_MACHINE}-unknown-atheos exit ;; i*86:syllable:*:*) echo ${UNAME_MACHINE}-pc-syllable exit ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) echo i386-unknown-lynxos${UNAME_RELEASE} exit ;; i*86:*DOS:*:*) echo ${UNAME_MACHINE}-pc-msdosdjgpp exit ;; i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} else echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} fi exit ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case `/bin/uname -X | grep "^Machine"` in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} exit ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 echo ${UNAME_MACHINE}-pc-sco$UNAME_REL else echo ${UNAME_MACHINE}-pc-sysv32 fi exit ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub # prints for the "djgpp" host, or else GDB configury will decide that # this is a cross-build. echo i586-pc-msdosdjgpp exit ;; Intel:Mach:3*:*) echo i386-pc-mach3 exit ;; paragon:*:*:*) echo i860-intel-osf1 exit ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 fi exit ;; mini*:CTIX:SYS*5:*) # "miniframe" echo m68010-convergent-sysv exit ;; mc68k:UNIX:SYSTEM5:3.51m) echo m68k-convergent-sysv exit ;; M680?0:D-NIX:5.3:*) echo m68k-diab-dnix exit ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; NCR*:*:4.2:* | MPRAS*:*:4.2:*) OS_REL='.3' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) echo m68k-unknown-lynxos${UNAME_RELEASE} exit ;; mc68030:UNIX_System_V:4.*:*) echo m68k-atari-sysv4 exit ;; TSUNAMI:LynxOS:2.*:*) echo sparc-unknown-lynxos${UNAME_RELEASE} exit ;; rs6000:LynxOS:2.*:*) echo rs6000-unknown-lynxos${UNAME_RELEASE} exit ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) echo powerpc-unknown-lynxos${UNAME_RELEASE} exit ;; SM[BE]S:UNIX_SV:*:*) echo mips-dde-sysv${UNAME_RELEASE} exit ;; RM*:ReliantUNIX-*:*:*) echo mips-sni-sysv4 exit ;; RM*:SINIX-*:*:*) echo mips-sni-sysv4 exit ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` echo ${UNAME_MACHINE}-sni-sysv4 else echo ns32k-sni-sysv fi exit ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says echo i586-unisys-sysv4 exit ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm echo hppa1.1-stratus-sysv4 exit ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. echo i860-stratus-sysv4 exit ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. echo ${UNAME_MACHINE}-stratus-vos exit ;; *:VOS:*:*) # From Paul.Green@stratus.com. echo hppa1.1-stratus-vos exit ;; mc68*:A/UX:*:*) echo m68k-apple-aux${UNAME_RELEASE} exit ;; news*:NEWS-OS:6*:*) echo mips-sony-newsos6 exit ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if [ -d /usr/nec ]; then echo mips-nec-sysv${UNAME_RELEASE} else echo mips-unknown-sysv${UNAME_RELEASE} fi exit ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. echo powerpc-be-beos exit ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. echo powerpc-apple-beos exit ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. echo i586-pc-beos exit ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. echo i586-pc-haiku exit ;; SX-4:SUPER-UX:*:*) echo sx4-nec-superux${UNAME_RELEASE} exit ;; SX-5:SUPER-UX:*:*) echo sx5-nec-superux${UNAME_RELEASE} exit ;; SX-6:SUPER-UX:*:*) echo sx6-nec-superux${UNAME_RELEASE} exit ;; SX-7:SUPER-UX:*:*) echo sx7-nec-superux${UNAME_RELEASE} exit ;; SX-8:SUPER-UX:*:*) echo sx8-nec-superux${UNAME_RELEASE} exit ;; SX-8R:SUPER-UX:*:*) echo sx8r-nec-superux${UNAME_RELEASE} exit ;; Power*:Rhapsody:*:*) echo powerpc-apple-rhapsody${UNAME_RELEASE} exit ;; *:Rhapsody:*:*) echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} exit ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown case $UNAME_PROCESSOR in i386) eval $set_cc_for_build if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then UNAME_PROCESSOR="x86_64" fi fi ;; unknown) UNAME_PROCESSOR=powerpc ;; esac echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} exit ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = "x86"; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} exit ;; *:QNX:*:4*) echo i386-pc-qnx exit ;; NEO-?:NONSTOP_KERNEL:*:*) echo neo-tandem-nsk${UNAME_RELEASE} exit ;; NSE-?:NONSTOP_KERNEL:*:*) echo nse-tandem-nsk${UNAME_RELEASE} exit ;; NSR-?:NONSTOP_KERNEL:*:*) echo nsr-tandem-nsk${UNAME_RELEASE} exit ;; *:NonStop-UX:*:*) echo mips-compaq-nonstopux exit ;; BS2000:POSIX*:*:*) echo bs2000-siemens-sysv exit ;; DS/*:UNIX_System_V:*:*) echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} exit ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. if test "$cputype" = "386"; then UNAME_MACHINE=i386 else UNAME_MACHINE="$cputype" fi echo ${UNAME_MACHINE}-unknown-plan9 exit ;; *:TOPS-10:*:*) echo pdp10-unknown-tops10 exit ;; *:TENEX:*:*) echo pdp10-unknown-tenex exit ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) echo pdp10-dec-tops20 exit ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) echo pdp10-xkl-tops20 exit ;; *:TOPS-20:*:*) echo pdp10-unknown-tops20 exit ;; *:ITS:*:*) echo pdp10-unknown-its exit ;; SEI:*:*:SEIUX) echo mips-sei-seiux${UNAME_RELEASE} exit ;; *:DragonFly:*:*) echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` exit ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` case "${UNAME_MACHINE}" in A*) echo alpha-dec-vms ; exit ;; I*) echo ia64-dec-vms ; exit ;; V*) echo vax-dec-vms ; exit ;; esac ;; *:XENIX:*:SysV) echo i386-pc-xenix exit ;; i*86:skyos:*:*) echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' exit ;; i*86:rdos:*:*) echo ${UNAME_MACHINE}-pc-rdos exit ;; i*86:AROS:*:*) echo ${UNAME_MACHINE}-pc-aros exit ;; x86_64:VMkernel:*:*) echo ${UNAME_MACHINE}-unknown-esx exit ;; esac #echo '(No uname command or uname output not recognized.)' 1>&2 #echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 eval $set_cc_for_build cat >$dummy.c < # include #endif main () { #if defined (sony) #if defined (MIPSEB) /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, I don't know.... */ printf ("mips-sony-bsd\n"); exit (0); #else #include printf ("m68k-sony-newsos%s\n", #ifdef NEWSOS4 "4" #else "" #endif ); exit (0); #endif #endif #if defined (__arm) && defined (__acorn) && defined (__unix) printf ("arm-acorn-riscix\n"); exit (0); #endif #if defined (hp300) && !defined (hpux) printf ("m68k-hp-bsd\n"); exit (0); #endif #if defined (NeXT) #if !defined (__ARCHITECTURE__) #define __ARCHITECTURE__ "m68k" #endif int version; version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; if (version < 4) printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); else printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); exit (0); #endif #if defined (MULTIMAX) || defined (n16) #if defined (UMAXV) printf ("ns32k-encore-sysv\n"); exit (0); #else #if defined (CMU) printf ("ns32k-encore-mach\n"); exit (0); #else printf ("ns32k-encore-bsd\n"); exit (0); #endif #endif #endif #if defined (__386BSD__) printf ("i386-pc-bsd\n"); exit (0); #endif #if defined (sequent) #if defined (i386) printf ("i386-sequent-dynix\n"); exit (0); #endif #if defined (ns32000) printf ("ns32k-sequent-dynix\n"); exit (0); #endif #endif #if defined (_SEQUENT_) struct utsname un; uname(&un); if (strncmp(un.version, "V2", 2) == 0) { printf ("i386-sequent-ptx2\n"); exit (0); } if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ printf ("i386-sequent-ptx1\n"); exit (0); } printf ("i386-sequent-ptx\n"); exit (0); #endif #if defined (vax) # if !defined (ultrix) # include # if defined (BSD) # if BSD == 43 printf ("vax-dec-bsd4.3\n"); exit (0); # else # if BSD == 199006 printf ("vax-dec-bsd4.3reno\n"); exit (0); # else printf ("vax-dec-bsd\n"); exit (0); # endif # endif # else printf ("vax-dec-bsd\n"); exit (0); # endif # else printf ("vax-dec-ultrix\n"); exit (0); # endif #endif #if defined (alliant) && defined (i860) printf ("i860-alliant-bsd\n"); exit (0); #endif exit (1); } EOF $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` && { echo "$SYSTEM_NAME"; exit; } # Apollos put the system type in the environment. test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; } # Convex versions that predate uname can use getsysinfo(1) if [ -x /usr/convex/getsysinfo ] then case `getsysinfo -f cpu_type` in c1*) echo c1-convex-bsd exit ;; c2*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; c34*) echo c34-convex-bsd exit ;; c38*) echo c38-convex-bsd exit ;; c4*) echo c4-convex-bsd exit ;; esac fi cat >&2 < in order to provide the needed information to handle your system. config.guess timestamp = $timestamp uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` /bin/uname -X = `(/bin/uname -X) 2>/dev/null` hostinfo = `(hostinfo) 2>/dev/null` /bin/universe = `(/bin/universe) 2>/dev/null` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` /bin/arch = `(/bin/arch) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = ${UNAME_MACHINE} UNAME_RELEASE = ${UNAME_RELEASE} UNAME_SYSTEM = ${UNAME_SYSTEM} UNAME_VERSION = ${UNAME_VERSION} EOF exit 1 # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: pgbouncer-1.7/install-sh0000755000175000017500000003253712635051441012305 00000000000000#!/bin/sh # install - install a program, script, or datafile scriptversion=2009-04-28.21; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the # following copyright and license. # # Copyright (C) 1994 X Consortium # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the name of the X Consortium shall not # be used in advertising or otherwise to promote the sale, use or other deal- # ings in this Software without prior written authorization from the X Consor- # tium. # # # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent # `make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. nl=' ' IFS=" "" $nl" # set DOITPROG to echo to test this script # Don't use :- since 4.3BSD and earlier shells don't like it. doit=${DOITPROG-} if test -z "$doit"; then doit_exec=exec else doit_exec=$doit fi # Put in absolute file names if you don't have them in your path; # or use environment vars. chgrpprog=${CHGRPPROG-chgrp} chmodprog=${CHMODPROG-chmod} chownprog=${CHOWNPROG-chown} cmpprog=${CMPPROG-cmp} cpprog=${CPPROG-cp} mkdirprog=${MKDIRPROG-mkdir} mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} posix_glob='?' initialize_posix_glob=' test "$posix_glob" != "?" || { if (set -f) 2>/dev/null; then posix_glob= else posix_glob=: fi } ' posix_mkdir= # Desired mode of installed file. mode=0755 chgrpcmd= chmodcmd=$chmodprog chowncmd= mvcmd=$mvprog rmcmd="$rmprog -f" stripcmd= src= dst= dir_arg= dst_arg= copy_on_change=false no_target_directory= usage="\ Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE or: $0 [OPTION]... SRCFILES... DIRECTORY or: $0 [OPTION]... -t DIRECTORY SRCFILES... or: $0 [OPTION]... -d DIRECTORIES... In the 1st form, copy SRCFILE to DSTFILE. In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. In the 4th, create DIRECTORIES. Options: --help display this help and exit. --version display version info and exit. -c (ignored) -C install only if different (preserve the last data modification time) -d create directories instead of installing files. -g GROUP $chgrpprog installed files to GROUP. -m MODE $chmodprog installed files to MODE. -o USER $chownprog installed files to USER. -s $stripprog installed files. -t DIRECTORY install into DIRECTORY. -T report an error if DSTFILE is a directory. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG " while test $# -ne 0; do case $1 in -c) ;; -C) copy_on_change=true;; -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 case $mode in *' '* | *' '* | *' '* | *'*'* | *'?'* | *'['*) echo "$0: invalid mode: $mode" >&2 exit 1;; esac shift;; -o) chowncmd="$chownprog $2" shift;; -s) stripcmd=$stripprog;; -t) dst_arg=$2 shift;; -T) no_target_directory=true;; --version) echo "$0 $scriptversion"; exit $?;; --) shift break;; -*) echo "$0: invalid option: $1" >&2 exit 1;; *) break;; esac shift done if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. # Otherwise, the last argument is the destination. Remove it from $@. for arg do if test -n "$dst_arg"; then # $@ is not empty: it contains at least $arg. set fnord "$@" "$dst_arg" shift # fnord fi shift # arg dst_arg=$arg done fi if test $# -eq 0; then if test -z "$dir_arg"; then echo "$0: no input file specified." >&2 exit 1 fi # It's OK to call `install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi if test -z "$dir_arg"; then trap '(exit $?); exit' 1 2 13 15 # Set umask so as not to create temps with too-generous modes. # However, 'strip' requires both read and write access to temps. case $mode in # Optimize common cases. *644) cp_umask=133;; *755) cp_umask=22;; *[0-7]) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac fi for src do # Protect names starting with `-'. case $src in -*) src=./$src;; esac if test -n "$dir_arg"; then dst=$src dstdir=$dst test -d "$dstdir" dstdir_status=$? else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if test ! -f "$src" && test ! -d "$src"; then echo "$0: $src does not exist." >&2 exit 1 fi if test -z "$dst_arg"; then echo "$0: no destination specified." >&2 exit 1 fi dst=$dst_arg # Protect names starting with `-'. case $dst in -*) dst=./$dst;; esac # If destination is a directory, append the input filename; won't work # if double slashes aren't ignored. if test -d "$dst"; then if test -n "$no_target_directory"; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst dst=$dstdir/`basename "$src"` dstdir_status=0 else # Prefer dirname, but fall back on a substitute if dirname fails. dstdir=` (dirname "$dst") 2>/dev/null || expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$dst" : 'X\(//\)[^/]' \| \ X"$dst" : 'X\(//\)$' \| \ X"$dst" : 'X\(/\)' \| . 2>/dev/null || echo X"$dst" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q' ` test -d "$dstdir" dstdir_status=$? fi fi obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') # Create intermediate dirs using mode 755 as modified by the umask. # This is like FreeBSD 'install' as of 1997-10-28. umask=`umask` case $stripcmd.$umask in # Optimize common cases. *[2367][2367]) mkdir_umask=$umask;; .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; *[0-7]) mkdir_umask=`expr $umask + 22 \ - $umask % 100 % 40 + $umask % 20 \ - $umask % 10 % 4 + $umask % 2 `;; *) mkdir_umask=$umask,go-w;; esac # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then mkdir_mode=-m$mode else mkdir_mode= fi posix_mkdir=false case $umask in *[123567][0-7][0-7]) # POSIX mkdir -p sets u+wx bits regardless of umask, which # is incompatible with FreeBSD 'install' when (umask & 300) != 0. ;; *) tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 if (umask $mkdir_umask && exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 then if test -z "$dir_arg" || { # Check for POSIX incompatibilities with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or # other-writeable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. ls_ld_tmpdir=`ls -ld "$tmpdir"` case $ls_ld_tmpdir in d????-?r-*) different_mode=700;; d????-?--*) different_mode=755;; *) false;; esac && $mkdirprog -m$different_mode -p -- "$tmpdir" && { ls_ld_tmpdir_1=`ls -ld "$tmpdir"` test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" } } then posix_mkdir=: fi rmdir "$tmpdir/d" "$tmpdir" else # Remove any dirs left behind by ancient mkdir implementations. rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null fi trap '' 0;; esac;; esac if $posix_mkdir && ( umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else # The umask is ridiculous, or mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. case $dstdir in /*) prefix='/';; -*) prefix='./';; *) prefix='';; esac eval "$initialize_posix_glob" oIFS=$IFS IFS=/ $posix_glob set -f set fnord $dstdir shift $posix_glob set +f IFS=$oIFS prefixes= for d do test -z "$d" && continue prefix=$prefix$d if test -d "$prefix"; then prefixes= else if $posix_mkdir; then (umask=$mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 else case $prefix in *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; *) qprefix=$prefix;; esac prefixes="$prefixes '$qprefix'" fi fi prefix=$prefix/ done if test -n "$prefixes"; then # Don't fail if two instances are running concurrently. (umask $mkdir_umask && eval "\$doit_exec \$mkdirprog $prefixes") || test -d "$dstdir" || exit 1 obsolete_mkdir_used=true fi fi fi if test -n "$dir_arg"; then { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 else # Make a couple of temp file names in the proper directory. dsttmp=$dstdir/_inst.$$_ rmtmp=$dstdir/_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $cpprog $src $dsttmp" command. # { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && eval "$initialize_posix_glob" && $posix_glob set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && $posix_glob set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then rm -f "$dsttmp" else # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || # The rename failed, perhaps because mv can't rename something else # to itself, or perhaps because mv is so ancient that it does not # support -f. { # Now remove or move aside any old file at destination location. # We try this two ways since rm can't unlink itself on some # systems and the destination file might be busy for other # reasons. In this case, the final cleanup might fail but the new # file should still install successfully. { test ! -f "$dst" || $doit $rmcmd -f "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } } || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 } } && # Now rename the file to the real destination. $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 trap '' 0 fi done # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: