| 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 |  | 
|---|
| 18 | static void | 
|---|
| 19 | help(const char *progname); | 
|---|
| 20 |  | 
|---|
| 21 | int | 
|---|
| 22 | main(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 |  | 
|---|
| 220 | static void | 
|---|
| 221 | help(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 |  | 
|---|