]> granicus.if.org Git - postgresql/blob - src/bin/pg_basebackup/streamutil.c
1b4a9d240b3d57e4c84c123cac966f9e76269870
[postgresql] / src / bin / pg_basebackup / streamutil.c
1 /*-------------------------------------------------------------------------
2  *
3  * streamutil.c - utility functions for pg_basebackup and pg_receivelog
4  *
5  * Author: Magnus Hagander <magnus@hagander.net>
6  *
7  * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
8  *
9  * IDENTIFICATION
10  *                src/bin/pg_basebackup/streamutil.c
11  *-------------------------------------------------------------------------
12  */
13
14 /*
15  * We have to use postgres.h not postgres_fe.h here, because there's so much
16  * backend-only stuff in the XLOG include files we need.  But we need a
17  * frontend-ish environment otherwise.  Hence this ugly hack.
18  */
19 #define FRONTEND 1
20 #include "postgres.h"
21 #include "streamutil.h"
22
23 #include <stdio.h>
24 #include <string.h>
25
26 const char *progname;
27 char       *dbhost = NULL;
28 char       *dbuser = NULL;
29 char       *dbport = NULL;
30 int                     dbgetpassword = 0;      /* 0=auto, -1=never, 1=always */
31 static char *dbpassword = NULL;
32 PGconn     *conn = NULL;
33
34 /*
35  * strdup() and malloc() replacements that prints an error and exits
36  * if something goes wrong. Can never return NULL.
37  */
38 char *
39 xstrdup(const char *s)
40 {
41         char       *result;
42
43         result = strdup(s);
44         if (!result)
45         {
46                 fprintf(stderr, _("%s: out of memory\n"), progname);
47                 exit(1);
48         }
49         return result;
50 }
51
52 void *
53 xmalloc0(int size)
54 {
55         void       *result;
56
57         result = malloc(size);
58         if (!result)
59         {
60                 fprintf(stderr, _("%s: out of memory\n"), progname);
61                 exit(1);
62         }
63         MemSet(result, 0, size);
64         return result;
65 }
66
67
68 /*
69  * Connect to the server. Returns a valid PGconn pointer if connected,
70  * or NULL on non-permanent error. On permanent error, the function will
71  * call exit(1) directly.
72  */
73 PGconn *
74 GetConnection(void)
75 {
76         PGconn     *tmpconn;
77         int                     argcount = 4;   /* dbname, replication, fallback_app_name,
78                                                                  * password */
79         int                     i;
80         const char **keywords;
81         const char **values;
82         char       *password = NULL;
83         const char *tmpparam;
84
85         if (dbhost)
86                 argcount++;
87         if (dbuser)
88                 argcount++;
89         if (dbport)
90                 argcount++;
91
92         keywords = xmalloc0((argcount + 1) * sizeof(*keywords));
93         values = xmalloc0((argcount + 1) * sizeof(*values));
94
95         keywords[0] = "dbname";
96         values[0] = "replication";
97         keywords[1] = "replication";
98         values[1] = "true";
99         keywords[2] = "fallback_application_name";
100         values[2] = progname;
101         i = 3;
102         if (dbhost)
103         {
104                 keywords[i] = "host";
105                 values[i] = dbhost;
106                 i++;
107         }
108         if (dbuser)
109         {
110                 keywords[i] = "user";
111                 values[i] = dbuser;
112                 i++;
113         }
114         if (dbport)
115         {
116                 keywords[i] = "port";
117                 values[i] = dbport;
118                 i++;
119         }
120
121         while (true)
122         {
123                 if (password)
124                         free(password);
125
126                 if (dbpassword)
127                 {
128                         /*
129                          * We've saved a password when a previous connection succeeded,
130                          * meaning this is the call for a second session to the same
131                          * database, so just forcibly reuse that password.
132                          */
133                         keywords[argcount - 1] = "password";
134                         values[argcount - 1] = dbpassword;
135                         dbgetpassword = -1; /* Don't try again if this fails */
136                 }
137                 else if (dbgetpassword == 1)
138                 {
139                         password = simple_prompt(_("Password: "), 100, false);
140                         keywords[argcount - 1] = "password";
141                         values[argcount - 1] = password;
142                 }
143
144                 tmpconn = PQconnectdbParams(keywords, values, true);
145
146                 if (PQstatus(tmpconn) == CONNECTION_BAD &&
147                         PQconnectionNeedsPassword(tmpconn) &&
148                         dbgetpassword != -1)
149                 {
150                         dbgetpassword = 1;      /* ask for password next time */
151                         PQfinish(tmpconn);
152                         continue;
153                 }
154
155                 if (PQstatus(tmpconn) != CONNECTION_OK)
156                 {
157                         fprintf(stderr, _("%s: could not connect to server: %s\n"),
158                                         progname, PQerrorMessage(tmpconn));
159                         return NULL;
160                 }
161
162                 /* Connection ok! */
163                 free(values);
164                 free(keywords);
165
166                 /*
167                  * Ensure we have the same value of integer timestamps as the server
168                  * we are connecting to.
169                  */
170                 tmpparam = PQparameterStatus(tmpconn, "integer_datetimes");
171                 if (!tmpparam)
172                 {
173                         fprintf(stderr, _("%s: could not determine server setting for integer_datetimes\n"),
174                                         progname);
175                         PQfinish(tmpconn);
176                         exit(1);
177                 }
178
179 #ifdef HAVE_INT64_TIMESTAMP
180                 if (strcmp(tmpparam, "on") != 0)
181 #else
182                 if (strcmp(tmpparam, "off") != 0)
183 #endif
184                 {
185                         fprintf(stderr, _("%s: integer_datetimes compile flag does not match server\n"),
186                                         progname);
187                         PQfinish(tmpconn);
188                         exit(1);
189                 }
190
191                 /* Store the password for next run */
192                 if (password)
193                         dbpassword = password;
194                 return tmpconn;
195         }
196 }