2 * PgBouncer - Lightweight connection pooler for PostgreSQL.
4 * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 * Launcer for all the rest.
28 static bool set_mode(ConfElem
*elem
, const char *val
, PgSocket
*console
);
29 static const char *get_mode(ConfElem
*elem
);
30 static bool set_auth(ConfElem
*elem
, const char *val
, PgSocket
*console
);
31 static const char *get_auth(ConfElem
*elem
);
32 static bool set_defer_accept(ConfElem
*elem
, const char *val
, PgSocket
*console
);
34 static const char usage_str
[] =
35 "Usage: %s [OPTION]... config.ini\n"
36 " -d Run in background (as a daemon)\n"
37 " -R Do a online restart\n"
39 " -v Increase verbosity\n"
40 " -u <username> Assume identity of <username>\n"
42 " -h Show this help screen and exit\n";
44 static void usage(int err
, char *exe
)
46 printf(usage_str
, basename(exe
));
51 * configuration storage
54 int cf_quiet
= 0; /* if set, no log is printed to stdout/err */
57 int cf_pause_mode
= P_NONE
;
58 int cf_shutdown
= 0; /* 1 - wait for queries to finish, 2 - shutdown immediately */
61 static char *cf_username
= "";
62 char *cf_syslog_facility
= "daemon";
63 char *cf_config_file
= "";
65 char *cf_listen_addr
= NULL
;
66 int cf_listen_port
= 6432;
67 int cf_listen_backlog
= 128;
69 char *cf_unix_socket_dir
= "/tmp";
71 char *cf_unix_socket_dir
= "";
74 int cf_pool_mode
= POOL_SESSION
;
77 int cf_sbuf_len
= 2048;
78 int cf_sbuf_loopcnt
= 5;
79 int cf_tcp_socket_buffer
= 0;
80 #if defined(TCP_DEFER_ACCEPT) || defined(SO_ACCEPTFILTER)
81 int cf_tcp_defer_accept
= 1;
83 int cf_tcp_defer_accept
= 0;
85 int cf_tcp_keepalive
= 0;
86 int cf_tcp_keepcnt
= 0;
87 int cf_tcp_keepidle
= 0;
88 int cf_tcp_keepintvl
= 0;
90 int cf_auth_type
= AUTH_MD5
;
91 char *cf_auth_file
= "unconfigured_file";
93 int cf_max_client_conn
= 100;
94 int cf_default_pool_size
= 20;
95 int cf_res_pool_size
= 0;
96 usec_t cf_res_pool_timeout
= 5;
98 char *cf_server_reset_query
= "";
99 char *cf_server_check_query
= "select 1";
100 usec_t cf_server_check_delay
= 30 * USEC
;
101 int cf_server_round_robin
= 0;
103 char *cf_ignore_startup_params
= "";
105 char *cf_autodb_connstr
= NULL
; /* here is "" different from NULL */
107 usec_t cf_autodb_idle_timeout
= 3600*USEC
;
109 usec_t cf_server_lifetime
= 60*60*USEC
;
110 usec_t cf_server_idle_timeout
= 10*60*USEC
;
111 usec_t cf_server_connect_timeout
= 15*USEC
;
112 usec_t cf_server_login_retry
= 15*USEC
;
113 usec_t cf_query_timeout
= 0*USEC
;
114 usec_t cf_query_wait_timeout
= 0*USEC
;
115 usec_t cf_client_idle_timeout
= 0*USEC
;
116 usec_t cf_client_login_timeout
= 60*USEC
;
117 usec_t cf_suspend_timeout
= 10*USEC
;
119 usec_t g_suspend_start
= 0;
121 char *cf_logfile
= "";
122 char *cf_pidfile
= "";
123 char *cf_jobname
= "pgbouncer";
125 char *cf_admin_users
= "";
126 char *cf_stats_users
= "";
127 int cf_stats_period
= 60;
129 int cf_log_connections
= 1;
130 int cf_log_disconnections
= 1;
131 int cf_log_pooler_errors
= 1;
134 * config file description
136 ConfElem bouncer_params
[] = {
137 {"job_name", false, CF_STR
, &cf_jobname
},
139 {"service_name", false, CF_STR
, &cf_jobname
}, /* alias for job_name */
141 {"conffile", true, CF_STR
, &cf_config_file
},
142 {"logfile", true, CF_STR
, &cf_logfile
},
143 {"pidfile", false, CF_STR
, &cf_pidfile
},
144 {"listen_addr", false, CF_STR
, &cf_listen_addr
},
145 {"listen_port", false, CF_INT
, &cf_listen_port
},
146 {"listen_backlog", false, CF_INT
, &cf_listen_backlog
},
148 {"unix_socket_dir", false, CF_STR
, &cf_unix_socket_dir
},
150 {"auth_type", true, {get_auth
, set_auth
}},
151 {"auth_file", true, CF_STR
, &cf_auth_file
},
152 {"pool_mode", true, {get_mode
, set_mode
}},
153 {"max_client_conn", true, CF_INT
, &cf_max_client_conn
},
154 {"default_pool_size", true, CF_INT
, &cf_default_pool_size
},
155 {"reserve_pool_size", true, CF_INT
, &cf_res_pool_size
},
156 {"reserve_pool_timeout",true, CF_INT
, &cf_res_pool_timeout
},
157 {"syslog", true, CF_INT
, &cf_syslog
},
158 {"syslog_facility", true, CF_STR
, &cf_syslog_facility
},
160 {"user", false, CF_STR
, &cf_username
},
163 {"autodb_idle_timeout", true, CF_TIME
, &cf_autodb_idle_timeout
},
165 {"server_reset_query", true, CF_STR
, &cf_server_reset_query
},
166 {"server_check_query", true, CF_STR
, &cf_server_check_query
},
167 {"server_check_delay", true, CF_TIME
, &cf_server_check_delay
},
168 {"query_timeout", true, CF_TIME
, &cf_query_timeout
},
169 {"query_wait_timeout", true, CF_TIME
, &cf_query_wait_timeout
},
170 {"client_idle_timeout", true, CF_TIME
, &cf_client_idle_timeout
},
171 {"client_login_timeout",true, CF_TIME
, &cf_client_login_timeout
},
172 {"server_lifetime", true, CF_TIME
, &cf_server_lifetime
},
173 {"server_idle_timeout", true, CF_TIME
, &cf_server_idle_timeout
},
174 {"server_connect_timeout",true, CF_TIME
, &cf_server_connect_timeout
},
175 {"server_login_retry", true, CF_TIME
, &cf_server_login_retry
},
176 {"server_round_robin", true, CF_INT
, &cf_server_round_robin
},
177 {"suspend_timeout", true, CF_TIME
, &cf_suspend_timeout
},
178 {"ignore_startup_parameters", true, CF_STR
, &cf_ignore_startup_params
},
180 {"pkt_buf", false, CF_INT
, &cf_sbuf_len
},
181 {"sbuf_loopcnt", true, CF_INT
, &cf_sbuf_loopcnt
},
182 {"tcp_defer_accept", true, {cf_get_int
, set_defer_accept
}, &cf_tcp_defer_accept
},
183 {"tcp_socket_buffer", true, CF_INT
, &cf_tcp_socket_buffer
},
184 {"tcp_keepalive", true, CF_INT
, &cf_tcp_keepalive
},
185 {"tcp_keepcnt", true, CF_INT
, &cf_tcp_keepcnt
},
186 {"tcp_keepidle", true, CF_INT
, &cf_tcp_keepidle
},
187 {"tcp_keepintvl", true, CF_INT
, &cf_tcp_keepintvl
},
188 {"verbose", true, CF_INT
, &cf_verbose
},
189 {"admin_users", true, CF_STR
, &cf_admin_users
},
190 {"stats_users", true, CF_STR
, &cf_stats_users
},
191 {"stats_period", true, CF_INT
, &cf_stats_period
},
192 {"log_connections", true, CF_INT
, &cf_log_connections
},
193 {"log_disconnections", true, CF_INT
, &cf_log_disconnections
},
194 {"log_pooler_errors", true, CF_INT
, &cf_log_pooler_errors
},
198 static ConfSection bouncer_config
[] = {
199 {"pgbouncer", bouncer_params
, NULL
},
200 {"databases", NULL
, parse_database
},
204 static const char *get_mode(ConfElem
*elem
)
206 switch (cf_pool_mode
) {
207 case POOL_STMT
: return "statement";
208 case POOL_TX
: return "transaction";
209 case POOL_SESSION
: return "session";
211 fatal("borken mode? should not happen");
216 static bool set_mode(ConfElem
*elem
, const char *val
, PgSocket
*console
)
218 if (strcasecmp(val
, "session") == 0)
219 cf_pool_mode
= POOL_SESSION
;
220 else if (strcasecmp(val
, "transaction") == 0)
221 cf_pool_mode
= POOL_TX
;
222 else if (strcasecmp(val
, "statement") == 0)
223 cf_pool_mode
= POOL_STMT
;
225 admin_error(console
, "bad mode: %s", val
);
231 static const char *get_auth(ConfElem
*elem
)
233 switch (cf_auth_type
) {
234 case AUTH_ANY
: return "any";
235 case AUTH_TRUST
: return "trust";
236 case AUTH_PLAIN
: return "plain";
237 case AUTH_CRYPT
: return "crypt";
238 case AUTH_MD5
: return "md5";
240 fatal("borken auth? should not happen");
245 static bool set_auth(ConfElem
*elem
, const char *val
, PgSocket
*console
)
247 if (strcasecmp(val
, "any") == 0)
248 cf_auth_type
= AUTH_ANY
;
249 else if (strcasecmp(val
, "trust") == 0)
250 cf_auth_type
= AUTH_TRUST
;
251 else if (strcasecmp(val
, "plain") == 0)
252 cf_auth_type
= AUTH_PLAIN
;
254 else if (strcasecmp(val
, "crypt") == 0)
255 cf_auth_type
= AUTH_CRYPT
;
257 else if (strcasecmp(val
, "md5") == 0)
258 cf_auth_type
= AUTH_MD5
;
260 admin_error(console
, "bad auth type: %s", val
);
266 static bool set_defer_accept(ConfElem
*elem
, const char *val
, PgSocket
*console
)
269 int oldval
= cf_tcp_defer_accept
;
270 ok
= cf_set_int(elem
, val
, console
);
271 if (ok
&& !!oldval
!= !!cf_tcp_defer_accept
)
272 pooler_tune_accept(cf_tcp_defer_accept
);
276 static void set_dbs_dead(bool flag
)
281 statlist_for_each(item
, &database_list
) {
282 db
= container_of(item
, PgDatabase
, head
);
291 /* config loading, tries to be tolerant to errors */
292 void load_config(bool reload
)
299 ok
= iniparser(cf_config_file
, bouncer_config
, reload
);
301 /* load users if needed */
302 if (cf_auth_type
>= AUTH_TRUST
)
303 load_auth_file(cf_auth_file
);
305 /* reset pool_size, kill dbs */
306 config_postprocess();
308 /* if ini file missing, dont kill anybody */
320 * handle_* functions are not actual signal handlers but called from
321 * event_loop() so they have no restrictions what they can do.
323 static struct event ev_sigterm
;
324 static struct event ev_sigint
;
326 static void handle_sigterm(int sock
, short flags
, void *arg
)
328 log_info("Got SIGTERM, fast exit");
329 /* pidfile cleanup happens via atexit() */
333 static void handle_sigint(int sock
, short flags
, void *arg
)
335 log_info("Got SIGINT, shutting down");
337 fatal("Takeover was in progress, going down immediately");
338 if (cf_pause_mode
== P_SUSPEND
)
339 fatal("Suspend was in progress, going down immediately");
340 cf_pause_mode
= P_PAUSE
;
346 static struct event ev_sigusr1
;
347 static struct event ev_sigusr2
;
348 static struct event ev_sighup
;
350 static void handle_sigusr1(int sock
, short flags
, void *arg
)
352 if (cf_pause_mode
== P_NONE
) {
353 log_info("Got SIGUSR1, pausing all activity");
354 cf_pause_mode
= P_PAUSE
;
356 log_info("Got SIGUSR1, but already paused/suspended");
360 static void handle_sigusr2(int sock
, short flags
, void *arg
)
362 switch (cf_pause_mode
) {
364 log_info("Got SIGUSR2, continuing from SUSPEND");
366 cf_pause_mode
= P_NONE
;
369 log_info("Got SIGUSR2, continuing from PAUSE");
370 cf_pause_mode
= P_NONE
;
373 log_info("Got SIGUSR1, but not paused/suspended");
376 /* avoid surprise later if cf_shutdown stays set */
378 log_info("Canceling shutdown");
383 static void handle_sighup(int sock
, short flags
, void *arg
)
385 log_info("Got SIGHUP re-reading config");
390 static void signal_setup(void)
399 sigaddset(&set
, SIGPIPE
);
400 err
= sigprocmask(SIG_BLOCK
, &set
, NULL
);
402 fatal_perror("sigprocmask");
404 /* install handlers */
406 signal_set(&ev_sigusr1
, SIGUSR1
, handle_sigusr1
, NULL
);
407 err
= signal_add(&ev_sigusr1
, NULL
);
409 fatal_perror("signal_add");
411 signal_set(&ev_sigusr2
, SIGUSR2
, handle_sigusr2
, NULL
);
412 err
= signal_add(&ev_sigusr2
, NULL
);
414 fatal_perror("signal_add");
416 signal_set(&ev_sighup
, SIGHUP
, handle_sighup
, NULL
);
417 err
= signal_add(&ev_sighup
, NULL
);
419 fatal_perror("signal_add");
421 signal_set(&ev_sigterm
, SIGTERM
, handle_sigterm
, NULL
);
422 err
= signal_add(&ev_sigterm
, NULL
);
424 fatal_perror("signal_add");
426 signal_set(&ev_sigint
, SIGINT
, handle_sigint
, NULL
);
427 err
= signal_add(&ev_sigint
, NULL
);
429 fatal_perror("signal_add");
435 static void go_daemon(void)
440 fatal("daemon needs pidfile configured");
442 /* dont log to stdout anymore */
445 /* send stdin, stdout, stderr to /dev/null */
446 fd
= open("/dev/null", O_RDWR
);
448 fatal_perror("/dev/null");
455 /* fork new process */
458 fatal_perror("fork");
462 /* create new session */
465 fatal_perror("setsid");
467 /* fork again to avoid being session leader */
470 fatal_perror("fork");
476 * pidfile management.
479 static void remove_pidfile(void)
486 static void check_pidfile(void)
496 /* check if pidfile exists */
497 if (stat(cf_pidfile
, &st
) < 0) {
499 fatal_perror("stat");
504 fd
= open(cf_pidfile
, O_RDONLY
);
507 res
= read(fd
, buf
, sizeof(buf
) - 1);
518 /* check if running */
519 if (kill(pid
, 0) >= 0)
524 /* seems the pidfile is not in use */
525 log_info("Stale pidfile, removing");
530 fatal("pidfile exists, another instance running?");
533 static void write_pidfile(void)
543 sprintf(buf
, "%u", (unsigned)pid
);
545 fd
= open(cf_pidfile
, O_WRONLY
| O_CREAT
| O_EXCL
, 0644);
547 fatal_perror("%s", cf_pidfile
);
548 res
= safe_write(fd
, buf
, strlen(buf
));
550 fatal_perror("%s", cf_pidfile
);
553 /* only remove when we have it actually written */
554 atexit(remove_pidfile
);
557 /* just print out max files, in the future may warn if something is off */
558 static void check_limits(void)
561 int total_users
= statlist_count(&user_list
);
567 log_noise("event: %d, SBuf: %d, PgSocket: %d, IOBuf: %d",
568 (int)sizeof(struct event
), (int)sizeof(SBuf
),
569 (int)sizeof(PgSocket
), (int)IOBUF_SIZE
);
572 err
= getrlimit(RLIMIT_NOFILE
, &lim
);
574 log_error("could not get RLIMIT_NOFILE: %s", strerror(errno
));
578 /* calculate theoretical max, +10 is just in case */
579 fd_count
= cf_max_client_conn
+ 10;
580 statlist_for_each(item
, &database_list
) {
581 db
= container_of(item
, PgDatabase
, head
);
583 fd_count
+= db
->pool_size
;
585 fd_count
+= db
->pool_size
* total_users
;
588 log_info("File descriptor limit: %d (H:%d), max_client_conn: %d, max fds possible: %d",
589 (int)lim
.rlim_cur
, (int)lim
.rlim_max
, cf_max_client_conn
, fd_count
);
592 static bool check_old_process_unix(void)
594 struct sockaddr_un sa_un
;
595 socklen_t len
= sizeof(sa_un
);
596 int domain
= AF_UNIX
;
599 if (!*cf_unix_socket_dir
)
602 memset(&sa_un
, 0, len
);
603 sa_un
.sun_family
= domain
;
604 snprintf(sa_un
.sun_path
, sizeof(sa_un
.sun_path
),
605 "%s/.s.PGSQL.%d", cf_unix_socket_dir
, cf_listen_port
);
607 fd
= socket(domain
, SOCK_STREAM
, 0);
609 fatal_perror("cannot create socket");
610 res
= safe_connect(fd
, (struct sockaddr
*)&sa_un
, len
);
617 static void main_loop_once(void)
623 err
= event_loop(EVLOOP_ONCE
);
626 log_warning("event_loop failed: %s", strerror(errno
));
629 reuse_just_freed_objects();
631 per_loop_pooler_maint();
634 static void takeover_part1(void)
636 /* use temporary libevent base */
637 void *evtmp
= event_init();
639 if (!*cf_unix_socket_dir
)
640 fatal("cannot reboot if unix dir not configured");
645 event_base_free(evtmp
);
648 /* boot everything */
649 int main(int argc
, char *argv
[])
652 bool did_takeover
= false;
653 char *arg_username
= NULL
;
656 while ((c
= getopt(argc
, argv
, "qvhdVRu:")) != EOF
) {
665 printf("%s\n", FULLVER
);
674 arg_username
= optarg
;
682 if (optind
+ 1 != argc
) {
683 fprintf(stderr
, "Need config file. See pgbouncer -h for usage.\n");
686 cf_config_file
= argv
[optind
];
692 /* prefer cmdline over config for username */
694 cf_username
= arg_username
;
696 /* switch user is needed */
698 change_user(cf_username
);
700 /* disallow running as root */
702 fatal("PgBouncer should not run as root");
704 /* need to do that after loading config */
710 if (check_old_process_unix()) {
714 log_info("old process not found, try to continue normally");
719 if (check_old_process_unix())
720 fatal("unix socket is in use, cannot continue");
727 /* initialize subsystems, order important */
728 srandom(time(NULL
) ^ getpid());
730 fatal("event_init() failed");
743 while (cf_shutdown
< 2)