text: /*-------------------------------------------------------------------------
   *
   *	common.c
   *		Common support routines for bin/scripts/
   *
   *
   * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
   * Portions Copyright (c) 1994, Regents of the University of California
   *
   * src/bin/scripts/common.c
   *
   *-------------------------------------------------------------------------
   */
  
  #include "postgres_fe.h"
  
  #include <pwd.h>
  #include <signal.h>
  #include <unistd.h>
  
  #include "common.h"
  #include "libpq/pqsignal.h"
  
  static void SetCancelConn(PGconn *conn);
  static void ResetCancelConn(void);
  
  static PGcancel *volatile cancelConn = NULL;
  
  #ifdef WIN32
  static CRITICAL_SECTION cancelConnLock;
  #endif
  
  /*
   * Returns the current user name.
   */
  const char *
  get_user_name(const char *progname)
  {
  #ifndef WIN32
  	struct passwd *pw;
  
  	pw = getpwuid(geteuid());
  	if (!pw)
  	{
  		fprintf(stderr, _("%s: could not obtain information about current user: %s\n"),
  				progname, strerror(errno));
  		exit(1);
  	}
  	return pw->pw_name;
  #else
  	static char username[128];	/* remains after function exit */
  	DWORD		len = sizeof(username) - 1;
  
  	if (!GetUserName(username, &len))
  	{
  		fprintf(stderr, _("%s: could not get current user name: %s\n"),
  				progname, strerror(errno));
  		exit(1);
  	}
  	return username;
  #endif
  }
  
  
  /*
   * Provide strictly harmonized handling of --help and --version
   * options.
   */
  void
  handle_help_version_opts(int argc, char *argv[],
  						 const char *fixed_progname, help_handler hlp)
  {
  	if (argc > 1)
  	{
  		if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
  		{
  			hlp(get_progname(argv[0]));
  			exit(0);
  		}
  		if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
  		{
  			printf("%s (Greenplum Database) " PG_VERSION "\n", fixed_progname);
  			exit(0);
  		}
  	}
  }
  
  
  /*
   * Make a database connection with the given parameters.  An
   * interactive password prompt is automatically issued if required.
   */
  PGconn *
  connectDatabase(const char *dbname, const char *pghost, const char *pgport,
  				const char *pguser, enum trivalue prompt_password,
  				const char *progname, bool fail_ok)
  {
  	PGconn	   *conn;
  	char	   *password = NULL;
  	bool		new_pass;
  
  	if (prompt_password == TRI_YES)
  		password = simple_prompt("Password: ", 100, false);
  
  	/*
  	 * Start the connection.  Loop until we have a password if requested by
  	 * backend.
  	 */
  	do
  	{
  #define PARAMS_ARRAY_SIZE	7
  		const char **keywords = malloc(PARAMS_ARRAY_SIZE * sizeof(*keywords));
  		const char **values = malloc(PARAMS_ARRAY_SIZE * sizeof(*values));
  
  		if (!keywords || !values)
  		{
  			fprintf(stderr, _("%s: out of memory\n"), progname);
  			exit(1);
  		}
  
  		keywords[0] = "host";
  		values[0] = pghost;
  		keywords[1] = "port";
  		values[1] = pgport;
  		keywords[2] = "user";
  		values[2] = pguser;
  		keywords[3] = "password";
  		values[3] = password;
  		keywords[4] = "dbname";
  		values[4] = dbname;
  		keywords[5] = "fallback_application_name";
  		values[5] = progname;
  		keywords[6] = NULL;
  		values[6] = NULL;
  
  		new_pass = false;
  		conn = PQconnectdbParams(keywords, values, true);
  
  		free(keywords);
  		free(values);
  
  		if (!conn)
  		{
  			fprintf(stderr, _("%s: could not connect to database %s\n"),
  					progname, dbname);
  			exit(1);
  		}
  
  		if (PQstatus(conn) == CONNECTION_BAD &&
  			PQconnectionNeedsPassword(conn) &&
  			password == NULL &&
  			prompt_password != TRI_NO)
  		{
  			PQfinish(conn);
  			password = simple_prompt("Password: ", 100, false);
  			new_pass = true;
  		}
  	} while (new_pass);
  
  	if (password)
  		free(password);
  
  	/* check to see that the backend connection was successfully made */
  	if (PQstatus(conn) == CONNECTION_BAD)
  	{
  		if (fail_ok)
  		{
  			PQfinish(conn);
  			return NULL;
  		}
  		fprintf(stderr, _("%s: could not connect to database %s: %s"),
  				progname, dbname, PQerrorMessage(conn));
  		exit(1);
  	}
  
  	return conn;
  }
  
  /*
   * Try to connect to the appropriate maintenance database.
   */
  PGconn *
  connectMaintenanceDatabase(const char *maintenance_db, const char *pghost,
  						   const char *pgport, const char *pguser,
  						   enum trivalue prompt_password,
  						   const char *progname)
  {
  	PGconn	   *conn;
  
  	/* If a maintenance database name was specified, just connect to it. */
  	if (maintenance_db)
  		return connectDatabase(maintenance_db, pghost, pgport, pguser,
  							   prompt_password, progname, false);
  
  	/* Otherwise, try postgres first and then template1. */
  	conn = connectDatabase("postgres", pghost, pgport, pguser, prompt_password,
  						   progname, true);
  	if (!conn)
  		conn = connectDatabase("template1", pghost, pgport, pguser,
  							   prompt_password, progname, false);
  
  	return conn;
  }
  
  /*
   * Run a query, return the results, exit program on failure.
   */
  PGresult *
  executeQuery(PGconn *conn, const char *query, const char *progname, bool echo)
  {
  	PGresult   *res;
  
  	if (echo)
  		printf("%s\n", query);
  
  	res = PQexec(conn, query);
  	if (!res ||
  		PQresultStatus(res) != PGRES_TUPLES_OK)
  	{
  		fprintf(stderr, _("%s: query failed: %s"),
  				progname, PQerrorMessage(conn));
  		fprintf(stderr, _("%s: query was: %s\n"),
  				progname, query);
  		PQfinish(conn);
  		exit(1);
  	}
  
  	return res;
  }
  
  
  /*
   * As above for a SQL command (which returns nothing).
   */
  void
  executeCommand(PGconn *conn, const char *query,
  			   const char *progname, bool echo)
  {
  	PGresult   *res;
  
  	if (echo)
  		printf("%s\n", query);
  
  	res = PQexec(conn, query);
  	if (!res ||
  		PQresultStatus(res) != PGRES_COMMAND_OK)
  	{
  		fprintf(stderr, _("%s: query failed: %s"),
  				progname, PQerrorMessage(conn));
  		fprintf(stderr, _("%s: query was: %s\n"),
  				progname, query);
  		PQfinish(conn);
  		exit(1);
  	}
  
  	PQclear(res);
  }
  
  
  /*
   * As above for a SQL maintenance command (returns command success).
   * Command is executed with a cancel handler set, so Ctrl-C can
   * interrupt it.
   */
  bool
  executeMaintenanceCommand(PGconn *conn, const char *query, bool echo)
  {
  	PGresult   *res;
  	bool		r;
  
  	if (echo)
  		printf("%s\n", query);
  
  	SetCancelConn(conn);
  	res = PQexec(conn, query);
  	ResetCancelConn();
  
  	r = (res && PQresultStatus(res) == PGRES_COMMAND_OK);
  
  	if (res)
  		PQclear(res);
  
  	return r;
  }
  
  /*
   * "Safe" wrapper around strdup().	Pulled from psql/common.c
   */
  char *
  pg_strdup(const char *string)
  {
  	char	   *tmp;
  
  	if (!string)
  	{
  		fprintf(stderr, _("pg_strdup: cannot duplicate null pointer (internal error)\n"));
  		exit(EXIT_FAILURE);
  	}
  	tmp = strdup(string);
  	if (!tmp)
  	{
  		fprintf(stderr, _("out of memory\n"));
  		exit(EXIT_FAILURE);
  	}
  	return tmp;
  }
  
  /*
   * Check yes/no answer in a localized way.	1=yes, 0=no, -1=neither.
   */
  
  /* translator: abbreviation for "yes" */
  #define PG_YESLETTER gettext_noop("y")
  /* translator: abbreviation for "no" */
  #define PG_NOLETTER gettext_noop("n")
  
  bool
  yesno_prompt(const char *question)
  {
  	char		prompt[256];
  
  	/*------
  	   translator: This is a question followed by the translated options for
  	   "yes" and "no". */
  	snprintf(prompt, sizeof(prompt), _("%s (%s/%s) "),
  			 _(question), _(PG_YESLETTER), _(PG_NOLETTER));
  
  	for (;;)
  	{
  		char	   *resp;
  
  		resp = simple_prompt(prompt, 1, true);
  
  		if (strcmp(resp, _(PG_YESLETTER)) == 0)
  		{
  			free(resp);
  			return true;
  		}
  		else if (strcmp(resp, _(PG_NOLETTER)) == 0)
  		{
  			free(resp);
  			return false;
  		}
  
  		free(resp);
  		printf(_("Please answer \"%s\" or \"%s\".\n"),
  			   _(PG_YESLETTER), _(PG_NOLETTER));
  	}
  }
  
  /*
   * SetCancelConn
   *
   * Set cancelConn to point to the current database connection.
   */
  static void
  SetCancelConn(PGconn *conn)
  {
  	PGcancel   *oldCancelConn;
  
  #ifdef WIN32
  	EnterCriticalSection(&cancelConnLock);
  #endif
  
  	/* Free the old one if we have one */
  	oldCancelConn = cancelConn;
  
  	/* be sure handle_sigint doesn't use pointer while freeing */
  	cancelConn = NULL;
  
  	if (oldCancelConn != NULL)
  		PQfreeCancel(oldCancelConn);
  
  	cancelConn = PQgetCancel(conn);
  
  #ifdef WIN32
  	LeaveCriticalSection(&cancelConnLock);
  #endif
  }
  
  /*
   * ResetCancelConn
   *
   * Free the current cancel connection, if any, and set to NULL.
   */
  static void
  ResetCancelConn(void)
  {
  	PGcancel   *oldCancelConn;
  
  #ifdef WIN32
  	EnterCriticalSection(&cancelConnLock);
  #endif
  
  	oldCancelConn = cancelConn;
  
  	/* be sure handle_sigint doesn't use pointer while freeing */
  	cancelConn = NULL;
  
  	if (oldCancelConn != NULL)
  		PQfreeCancel(oldCancelConn);
  
  #ifdef WIN32
  	LeaveCriticalSection(&cancelConnLock);
  #endif
  }
  
  #ifndef WIN32
  /*
   * Handle interrupt signals by canceling the current command,
   * if it's being executed through executeMaintenanceCommand(),
   * and thus has a cancelConn set.
   */
  static void
  handle_sigint(SIGNAL_ARGS)
  {
  	int			save_errno = errno;
  	char		errbuf[256];
  
  	/* Send QueryCancel if we are processing a database query */
  	if (cancelConn != NULL)
  	{
  		if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
  			fprintf(stderr, _("Cancel request sent\n"));
  		else
  			fprintf(stderr, _("Could not send cancel request: %s"), errbuf);
  	}
  
  	errno = save_errno;			/* just in case the write changed it */
  }
  
  void
  setup_cancel_handler(void)
  {
  	pqsignal(SIGINT, handle_sigint);
  }
  #else							/* WIN32 */
  
  /*
   * Console control handler for Win32. Note that the control handler will
   * execute on a *different thread* than the main one, so we need to do
   * proper locking around those structures.
   */
  static BOOL WINAPI
  consoleHandler(DWORD dwCtrlType)
  {
  	char		errbuf[256];
  
  	if (dwCtrlType == CTRL_C_EVENT ||
  		dwCtrlType == CTRL_BREAK_EVENT)
  	{
  		/* Send QueryCancel if we are processing a database query */
  		EnterCriticalSection(&cancelConnLock);
  		if (cancelConn != NULL)
  		{
  			if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
  				fprintf(stderr, _("Cancel request sent\n"));
  			else
  				fprintf(stderr, _("Could not send cancel request: %s"), errbuf);
  		}
  		LeaveCriticalSection(&cancelConnLock);
  
  		return TRUE;
  	}
  	else
  		/* Return FALSE for any signals not being handled */
  		return FALSE;
  }
  
  void
  setup_cancel_handler(void)
  {
  	InitializeCriticalSection(&cancelConnLock);
  
  	SetConsoleCtrlHandler(consoleHandler, TRUE);
  }
  
  #endif   /* WIN32 */
  