Header And Logo

PostgreSQL
| The world's most advanced open source database.

common.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  *  common.c
00004  *      Common support routines for bin/scripts/
00005  *
00006  *
00007  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00008  * Portions Copyright (c) 1994, Regents of the University of California
00009  *
00010  * src/bin/scripts/common.c
00011  *
00012  *-------------------------------------------------------------------------
00013  */
00014 
00015 #include "postgres_fe.h"
00016 
00017 #include <pwd.h>
00018 #include <signal.h>
00019 #include <unistd.h>
00020 
00021 #include "common.h"
00022 
00023 static void SetCancelConn(PGconn *conn);
00024 static void ResetCancelConn(void);
00025 
00026 static PGcancel *volatile cancelConn = NULL;
00027 
00028 #ifdef WIN32
00029 static CRITICAL_SECTION cancelConnLock;
00030 #endif
00031 
00032 /*
00033  * Returns the current user name.
00034  */
00035 const char *
00036 get_user_name(const char *progname)
00037 {
00038 #ifndef WIN32
00039     struct passwd *pw;
00040 
00041     pw = getpwuid(geteuid());
00042     if (!pw)
00043     {
00044         fprintf(stderr, _("%s: could not obtain information about current user: %s\n"),
00045                 progname, strerror(errno));
00046         exit(1);
00047     }
00048     return pw->pw_name;
00049 #else
00050     static char username[128];  /* remains after function exit */
00051     DWORD       len = sizeof(username) - 1;
00052 
00053     if (!GetUserName(username, &len))
00054     {
00055         fprintf(stderr, _("%s: could not get current user name: %s\n"),
00056                 progname, strerror(errno));
00057         exit(1);
00058     }
00059     return username;
00060 #endif
00061 }
00062 
00063 
00064 /*
00065  * Provide strictly harmonized handling of --help and --version
00066  * options.
00067  */
00068 void
00069 handle_help_version_opts(int argc, char *argv[],
00070                          const char *fixed_progname, help_handler hlp)
00071 {
00072     if (argc > 1)
00073     {
00074         if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
00075         {
00076             hlp(get_progname(argv[0]));
00077             exit(0);
00078         }
00079         if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
00080         {
00081             printf("%s (PostgreSQL) " PG_VERSION "\n", fixed_progname);
00082             exit(0);
00083         }
00084     }
00085 }
00086 
00087 
00088 /*
00089  * Make a database connection with the given parameters.  An
00090  * interactive password prompt is automatically issued if required.
00091  */
00092 PGconn *
00093 connectDatabase(const char *dbname, const char *pghost, const char *pgport,
00094                 const char *pguser, enum trivalue prompt_password,
00095                 const char *progname, bool fail_ok)
00096 {
00097     PGconn     *conn;
00098     char       *password = NULL;
00099     bool        new_pass;
00100 
00101     if (prompt_password == TRI_YES)
00102         password = simple_prompt("Password: ", 100, false);
00103 
00104     /*
00105      * Start the connection.  Loop until we have a password if requested by
00106      * backend.
00107      */
00108     do
00109     {
00110 #define PARAMS_ARRAY_SIZE   7
00111         const char **keywords = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*keywords));
00112         const char **values = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*values));
00113 
00114         keywords[0] = "host";
00115         values[0] = pghost;
00116         keywords[1] = "port";
00117         values[1] = pgport;
00118         keywords[2] = "user";
00119         values[2] = pguser;
00120         keywords[3] = "password";
00121         values[3] = password;
00122         keywords[4] = "dbname";
00123         values[4] = dbname;
00124         keywords[5] = "fallback_application_name";
00125         values[5] = progname;
00126         keywords[6] = NULL;
00127         values[6] = NULL;
00128 
00129         new_pass = false;
00130         conn = PQconnectdbParams(keywords, values, true);
00131 
00132         free(keywords);
00133         free(values);
00134 
00135         if (!conn)
00136         {
00137             fprintf(stderr, _("%s: could not connect to database %s\n"),
00138                     progname, dbname);
00139             exit(1);
00140         }
00141 
00142         if (PQstatus(conn) == CONNECTION_BAD &&
00143             PQconnectionNeedsPassword(conn) &&
00144             password == NULL &&
00145             prompt_password != TRI_NO)
00146         {
00147             PQfinish(conn);
00148             password = simple_prompt("Password: ", 100, false);
00149             new_pass = true;
00150         }
00151     } while (new_pass);
00152 
00153     if (password)
00154         free(password);
00155 
00156     /* check to see that the backend connection was successfully made */
00157     if (PQstatus(conn) == CONNECTION_BAD)
00158     {
00159         if (fail_ok)
00160         {
00161             PQfinish(conn);
00162             return NULL;
00163         }
00164         fprintf(stderr, _("%s: could not connect to database %s: %s"),
00165                 progname, dbname, PQerrorMessage(conn));
00166         exit(1);
00167     }
00168 
00169     return conn;
00170 }
00171 
00172 /*
00173  * Try to connect to the appropriate maintenance database.
00174  */
00175 PGconn *
00176 connectMaintenanceDatabase(const char *maintenance_db, const char *pghost,
00177                            const char *pgport, const char *pguser,
00178                            enum trivalue prompt_password,
00179                            const char *progname)
00180 {
00181     PGconn     *conn;
00182 
00183     /* If a maintenance database name was specified, just connect to it. */
00184     if (maintenance_db)
00185         return connectDatabase(maintenance_db, pghost, pgport, pguser,
00186                                prompt_password, progname, false);
00187 
00188     /* Otherwise, try postgres first and then template1. */
00189     conn = connectDatabase("postgres", pghost, pgport, pguser, prompt_password,
00190                            progname, true);
00191     if (!conn)
00192         conn = connectDatabase("template1", pghost, pgport, pguser,
00193                                prompt_password, progname, false);
00194 
00195     return conn;
00196 }
00197 
00198 /*
00199  * Run a query, return the results, exit program on failure.
00200  */
00201 PGresult *
00202 executeQuery(PGconn *conn, const char *query, const char *progname, bool echo)
00203 {
00204     PGresult   *res;
00205 
00206     if (echo)
00207         printf("%s\n", query);
00208 
00209     res = PQexec(conn, query);
00210     if (!res ||
00211         PQresultStatus(res) != PGRES_TUPLES_OK)
00212     {
00213         fprintf(stderr, _("%s: query failed: %s"),
00214                 progname, PQerrorMessage(conn));
00215         fprintf(stderr, _("%s: query was: %s\n"),
00216                 progname, query);
00217         PQfinish(conn);
00218         exit(1);
00219     }
00220 
00221     return res;
00222 }
00223 
00224 
00225 /*
00226  * As above for a SQL command (which returns nothing).
00227  */
00228 void
00229 executeCommand(PGconn *conn, const char *query,
00230                const char *progname, bool echo)
00231 {
00232     PGresult   *res;
00233 
00234     if (echo)
00235         printf("%s\n", query);
00236 
00237     res = PQexec(conn, query);
00238     if (!res ||
00239         PQresultStatus(res) != PGRES_COMMAND_OK)
00240     {
00241         fprintf(stderr, _("%s: query failed: %s"),
00242                 progname, PQerrorMessage(conn));
00243         fprintf(stderr, _("%s: query was: %s\n"),
00244                 progname, query);
00245         PQfinish(conn);
00246         exit(1);
00247     }
00248 
00249     PQclear(res);
00250 }
00251 
00252 
00253 /*
00254  * As above for a SQL maintenance command (returns command success).
00255  * Command is executed with a cancel handler set, so Ctrl-C can
00256  * interrupt it.
00257  */
00258 bool
00259 executeMaintenanceCommand(PGconn *conn, const char *query, bool echo)
00260 {
00261     PGresult   *res;
00262     bool        r;
00263 
00264     if (echo)
00265         printf("%s\n", query);
00266 
00267     SetCancelConn(conn);
00268     res = PQexec(conn, query);
00269     ResetCancelConn();
00270 
00271     r = (res && PQresultStatus(res) == PGRES_COMMAND_OK);
00272 
00273     if (res)
00274         PQclear(res);
00275 
00276     return r;
00277 }
00278 
00279 /*
00280  * Check yes/no answer in a localized way.  1=yes, 0=no, -1=neither.
00281  */
00282 
00283 /* translator: abbreviation for "yes" */
00284 #define PG_YESLETTER gettext_noop("y")
00285 /* translator: abbreviation for "no" */
00286 #define PG_NOLETTER gettext_noop("n")
00287 
00288 bool
00289 yesno_prompt(const char *question)
00290 {
00291     char        prompt[256];
00292 
00293     /*------
00294        translator: This is a question followed by the translated options for
00295        "yes" and "no". */
00296     snprintf(prompt, sizeof(prompt), _("%s (%s/%s) "),
00297              _(question), _(PG_YESLETTER), _(PG_NOLETTER));
00298 
00299     for (;;)
00300     {
00301         char       *resp;
00302 
00303         resp = simple_prompt(prompt, 1, true);
00304 
00305         if (strcmp(resp, _(PG_YESLETTER)) == 0)
00306         {
00307             free(resp);
00308             return true;
00309         }
00310         else if (strcmp(resp, _(PG_NOLETTER)) == 0)
00311         {
00312             free(resp);
00313             return false;
00314         }
00315 
00316         free(resp);
00317         printf(_("Please answer \"%s\" or \"%s\".\n"),
00318                _(PG_YESLETTER), _(PG_NOLETTER));
00319     }
00320 }
00321 
00322 /*
00323  * SetCancelConn
00324  *
00325  * Set cancelConn to point to the current database connection.
00326  */
00327 static void
00328 SetCancelConn(PGconn *conn)
00329 {
00330     PGcancel   *oldCancelConn;
00331 
00332 #ifdef WIN32
00333     EnterCriticalSection(&cancelConnLock);
00334 #endif
00335 
00336     /* Free the old one if we have one */
00337     oldCancelConn = cancelConn;
00338 
00339     /* be sure handle_sigint doesn't use pointer while freeing */
00340     cancelConn = NULL;
00341 
00342     if (oldCancelConn != NULL)
00343         PQfreeCancel(oldCancelConn);
00344 
00345     cancelConn = PQgetCancel(conn);
00346 
00347 #ifdef WIN32
00348     LeaveCriticalSection(&cancelConnLock);
00349 #endif
00350 }
00351 
00352 /*
00353  * ResetCancelConn
00354  *
00355  * Free the current cancel connection, if any, and set to NULL.
00356  */
00357 static void
00358 ResetCancelConn(void)
00359 {
00360     PGcancel   *oldCancelConn;
00361 
00362 #ifdef WIN32
00363     EnterCriticalSection(&cancelConnLock);
00364 #endif
00365 
00366     oldCancelConn = cancelConn;
00367 
00368     /* be sure handle_sigint doesn't use pointer while freeing */
00369     cancelConn = NULL;
00370 
00371     if (oldCancelConn != NULL)
00372         PQfreeCancel(oldCancelConn);
00373 
00374 #ifdef WIN32
00375     LeaveCriticalSection(&cancelConnLock);
00376 #endif
00377 }
00378 
00379 #ifndef WIN32
00380 /*
00381  * Handle interrupt signals by canceling the current command,
00382  * if it's being executed through executeMaintenanceCommand(),
00383  * and thus has a cancelConn set.
00384  */
00385 static void
00386 handle_sigint(SIGNAL_ARGS)
00387 {
00388     int         save_errno = errno;
00389     char        errbuf[256];
00390 
00391     /* Send QueryCancel if we are processing a database query */
00392     if (cancelConn != NULL)
00393     {
00394         if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
00395             fprintf(stderr, _("Cancel request sent\n"));
00396         else
00397             fprintf(stderr, _("Could not send cancel request: %s"), errbuf);
00398     }
00399 
00400     errno = save_errno;         /* just in case the write changed it */
00401 }
00402 
00403 void
00404 setup_cancel_handler(void)
00405 {
00406     pqsignal(SIGINT, handle_sigint);
00407 }
00408 #else                           /* WIN32 */
00409 
00410 /*
00411  * Console control handler for Win32. Note that the control handler will
00412  * execute on a *different thread* than the main one, so we need to do
00413  * proper locking around those structures.
00414  */
00415 static BOOL WINAPI
00416 consoleHandler(DWORD dwCtrlType)
00417 {
00418     char        errbuf[256];
00419 
00420     if (dwCtrlType == CTRL_C_EVENT ||
00421         dwCtrlType == CTRL_BREAK_EVENT)
00422     {
00423         /* Send QueryCancel if we are processing a database query */
00424         EnterCriticalSection(&cancelConnLock);
00425         if (cancelConn != NULL)
00426         {
00427             if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
00428                 fprintf(stderr, _("Cancel request sent\n"));
00429             else
00430                 fprintf(stderr, _("Could not send cancel request: %s"), errbuf);
00431         }
00432         LeaveCriticalSection(&cancelConnLock);
00433 
00434         return TRUE;
00435     }
00436     else
00437         /* Return FALSE for any signals not being handled */
00438         return FALSE;
00439 }
00440 
00441 void
00442 setup_cancel_handler(void)
00443 {
00444     InitializeCriticalSection(&cancelConnLock);
00445 
00446     SetConsoleCtrlHandler(consoleHandler, TRUE);
00447 }
00448 
00449 #endif   /* WIN32 */