1/*-------------------------------------------------------------------------
2 *
3 * pg_isready --- checks the status of the PostgreSQL server
4 *
5 * Copyright (c) 2013-2019, PostgreSQL Global Development Group
6 *
7 * src/bin/scripts/pg_isready.c
8 *
9 *-------------------------------------------------------------------------
10 */
11
12#include "postgres_fe.h"
13#include "common.h"
14#include "common/logging.h"
15
16#define DEFAULT_CONNECT_TIMEOUT "3"
17
18static void
19 help(const char *progname);
20
21int
22main(int argc, char **argv)
23{
24 int c;
25
26 const char *progname;
27
28 const char *pghost = NULL;
29 const char *pgport = NULL;
30 const char *pguser = NULL;
31 const char *pgdbname = NULL;
32 const char *connect_timeout = DEFAULT_CONNECT_TIMEOUT;
33
34 const char *pghost_str = NULL;
35 const char *pghostaddr_str = NULL;
36 const char *pgport_str = NULL;
37
38#define PARAMS_ARRAY_SIZE 7
39
40 const char *keywords[PARAMS_ARRAY_SIZE];
41 const char *values[PARAMS_ARRAY_SIZE];
42
43 bool quiet = false;
44
45 PGPing rv;
46 PQconninfoOption *opts = NULL;
47 PQconninfoOption *defs = NULL;
48 PQconninfoOption *opt;
49 PQconninfoOption *def;
50 char *errmsg = NULL;
51
52 /*
53 * We accept user and database as options to avoid useless errors from
54 * connecting with invalid params
55 */
56
57 static struct option long_options[] = {
58 {"dbname", required_argument, NULL, 'd'},
59 {"host", required_argument, NULL, 'h'},
60 {"port", required_argument, NULL, 'p'},
61 {"quiet", no_argument, NULL, 'q'},
62 {"timeout", required_argument, NULL, 't'},
63 {"username", required_argument, NULL, 'U'},
64 {NULL, 0, NULL, 0}
65 };
66
67 pg_logging_init(argv[0]);
68 progname = get_progname(argv[0]);
69 set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pgscripts"));
70 handle_help_version_opts(argc, argv, progname, help);
71
72 while ((c = getopt_long(argc, argv, "d:h:p:qt:U:", long_options, NULL)) != -1)
73 {
74 switch (c)
75 {
76 case 'd':
77 pgdbname = pg_strdup(optarg);
78 break;
79 case 'h':
80 pghost = pg_strdup(optarg);
81 break;
82 case 'p':
83 pgport = pg_strdup(optarg);
84 break;
85 case 'q':
86 quiet = true;
87 break;
88 case 't':
89 connect_timeout = pg_strdup(optarg);
90 break;
91 case 'U':
92 pguser = pg_strdup(optarg);
93 break;
94 default:
95 fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
96
97 /*
98 * We need to make sure we don't return 1 here because someone
99 * checking the return code might infer unintended meaning
100 */
101 exit(PQPING_NO_ATTEMPT);
102 }
103 }
104
105 if (optind < argc)
106 {
107 pg_log_error("too many command-line arguments (first is \"%s\")",
108 argv[optind]);
109 fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
110
111 /*
112 * We need to make sure we don't return 1 here because someone
113 * checking the return code might infer unintended meaning
114 */
115 exit(PQPING_NO_ATTEMPT);
116 }
117
118 keywords[0] = "host";
119 values[0] = pghost;
120 keywords[1] = "port";
121 values[1] = pgport;
122 keywords[2] = "user";
123 values[2] = pguser;
124 keywords[3] = "dbname";
125 values[3] = pgdbname;
126 keywords[4] = "connect_timeout";
127 values[4] = connect_timeout;
128 keywords[5] = "fallback_application_name";
129 values[5] = progname;
130 keywords[6] = NULL;
131 values[6] = NULL;
132
133 /*
134 * Get the host and port so we can display them in our output
135 */
136 if (pgdbname &&
137 (strncmp(pgdbname, "postgresql://", 13) == 0 ||
138 strncmp(pgdbname, "postgres://", 11) == 0 ||
139 strchr(pgdbname, '=') != NULL))
140 {
141 opts = PQconninfoParse(pgdbname, &errmsg);
142 if (opts == NULL)
143 {
144 pg_log_error("%s", errmsg);
145 exit(PQPING_NO_ATTEMPT);
146 }
147 }
148
149 defs = PQconndefaults();
150 if (defs == NULL)
151 {
152 pg_log_error("could not fetch default options");
153 exit(PQPING_NO_ATTEMPT);
154 }
155
156 for (opt = opts, def = defs; def->keyword; def++)
157 {
158 if (strcmp(def->keyword, "host") == 0)
159 {
160 if (opt && opt->val)
161 pghost_str = opt->val;
162 else if (pghost)
163 pghost_str = pghost;
164 else if (def->val)
165 pghost_str = def->val;
166 else
167 pghost_str = DEFAULT_PGSOCKET_DIR;
168 }
169 else if (strcmp(def->keyword, "hostaddr") == 0)
170 {
171 if (opt && opt->val)
172 pghostaddr_str = opt->val;
173 else if (def->val)
174 pghostaddr_str = def->val;
175 }
176 else if (strcmp(def->keyword, "port") == 0)
177 {
178 if (opt && opt->val)
179 pgport_str = opt->val;
180 else if (pgport)
181 pgport_str = pgport;
182 else if (def->val)
183 pgport_str = def->val;
184 }
185
186 if (opt)
187 opt++;
188 }
189
190 rv = PQpingParams(keywords, values, 1);
191
192 if (!quiet)
193 {
194 printf("%s:%s - ",
195 pghostaddr_str != NULL ? pghostaddr_str : pghost_str,
196 pgport_str);
197
198 switch (rv)
199 {
200 case PQPING_OK:
201 printf(_("accepting connections\n"));
202 break;
203 case PQPING_REJECT:
204 printf(_("rejecting connections\n"));
205 break;
206 case PQPING_NO_RESPONSE:
207 printf(_("no response\n"));
208 break;
209 case PQPING_NO_ATTEMPT:
210 printf(_("no attempt\n"));
211 break;
212 default:
213 printf(_("unknown\n"));
214 }
215 }
216
217 exit(rv);
218}
219
220static void
221help(const char *progname)
222{
223 printf(_("%s issues a connection check to a PostgreSQL database.\n\n"), progname);
224 printf(_("Usage:\n"));
225 printf(_(" %s [OPTION]...\n"), progname);
226
227 printf(_("\nOptions:\n"));
228 printf(_(" -d, --dbname=DBNAME database name\n"));
229 printf(_(" -q, --quiet run quietly\n"));
230 printf(_(" -V, --version output version information, then exit\n"));
231 printf(_(" -?, --help show this help, then exit\n"));
232
233 printf(_("\nConnection options:\n"));
234 printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
235 printf(_(" -p, --port=PORT database server port\n"));
236 printf(_(" -t, --timeout=SECS seconds to wait when attempting connection, 0 disables (default: %s)\n"), DEFAULT_CONNECT_TIMEOUT);
237 printf(_(" -U, --username=USERNAME user name to connect as\n"));
238 printf(_("\nReport bugs to <pgsql-bugs@lists.postgresql.org>.\n"));
239}
240