]> granicus.if.org Git - postgresql/blob - src/interfaces/libpq/fe-connect.c
Add PGUSER environment variable for client to specify Postgres username.
[postgresql] / src / interfaces / libpq / fe-connect.c
1 /*-------------------------------------------------------------------------
2  *
3  * fe-connect.c--
4  *    functions related to setting up a connection to the backend
5  *
6  * Copyright (c) 1994, Regents of the University of California
7  *
8  *
9  * IDENTIFICATION
10  *    $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.9 1996/10/10 08:20:09 bryanh Exp $
11  *
12  *-------------------------------------------------------------------------
13  */
14
15 #include <stdlib.h>
16 #include <sys/types.h>
17 #include <sys/socket.h>
18 #include <fcntl.h>
19 #include <unistd.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <netdb.h>
23 #include <errno.h>
24 #include <signal.h>
25 #include <libpq/pqcomm.h> /* for decls of MsgType, PacketBuf, StartupInfo */
26 #include <fe-auth.h>
27 #include "libpq-fe.h"
28
29 #if defined(PORTNAME_ultrix4) || defined(PORTNAME_next)
30   /* ultrix is lame and doesn't have strdup in libc for some reason */
31  /* [TRH] So doesn't NEXTSTEP.  But whaddaya expect for a non-ANSI  
32 standard function? (My, my. Touchy today, are we?) */
33 static
34 char *
35 strdup(const char *string)
36 {
37     char *nstr;
38
39   if ((nstr = malloc(strlen(string)+1)) != NULL)
40       strcpy(nstr, string);
41     return nstr;
42 }
43 #endif
44
45 /* use a local version instead of the one found in pqpacket.c */
46 static ConnStatusType connectDB(PGconn *conn);
47
48 static int packetSend(Port *port, PacketBuf *buf, PacketLen len,
49                       bool nonBlocking);
50 static void startup2PacketBuf(StartupInfo* s, PacketBuf* res);
51 static void freePGconn(PGconn *conn);
52 static void closePGconn(PGconn *conn);
53
54 #define NOTIFYLIST_INITIAL_SIZE 10
55 #define NOTIFYLIST_GROWBY 10
56
57 /* ----------------
58  *      PQsetdb
59  * 
60  * establishes a connection to a postgres backend through the postmaster
61  * at the specified host and port.
62  *
63  * returns a PGconn* which is needed for all subsequent libpq calls
64  * if the status field of the connection returned is CONNECTION_BAD,
65  * then some fields may be null'ed out instead of having valid values 
66  *
67  *  Uses these environment variables:
68  *
69  *    PGHOST       identifies host to which to connect if <pghost> argument
70  *                 is NULL or a null string.
71  *
72  *    PGPORT       identifies TCP port to which to connect if <pgport> argument
73  *                 is NULL or a null string.
74  *
75  *    PGTTY        identifies tty to which to send messages if <pgtty> argument
76  *                 is NULL or a null string.
77  *
78  *    PGOPTIONS    identifies connection options if <pgoptions> argument is
79  *                 NULL or a null string.
80  *
81  *    PGUSER       Postgres username to associate with the connection.
82  *
83  *    PGDATABASE   name of database to which to connect if <pgdatabase> 
84  *                 argument is NULL or a null string
85  *
86  *    None of the above need be defined.  There are defaults for all of them.
87  *
88  * ----------------
89  */
90 PGconn* 
91 PQsetdb(const char *pghost, const char* pgport, const char* pgoptions, const char* pgtty, const char* dbName)
92 {
93   PGconn *conn;
94   char *tmp;
95   char errorMessage[ERROR_MSG_LENGTH];
96     /* An error message from some service we call. */
97   bool error;   
98     /* We encountered an error that prevents successful completion */
99
100   conn = (PGconn*)malloc(sizeof(PGconn));
101
102   if (conn == NULL) 
103     fprintf(stderr,
104             "FATAL: PQsetdb() -- unable to allocate memory for a PGconn");
105   else {
106     conn->Pfout = NULL;
107     conn->Pfin = NULL;
108     conn->Pfdebug = NULL;
109     conn->port = NULL;
110     conn->notifyList = DLNewList();
111     
112     if (!pghost || pghost[0] == '\0') {
113       if (!(tmp = getenv("PGHOST"))) {
114         tmp = DefaultHost;
115       }
116       conn->pghost = strdup(tmp);
117     } else
118       conn->pghost = strdup(pghost);
119     
120     if (!pgport || pgport[0] == '\0') {
121       if (!(tmp = getenv("PGPORT"))) {
122         tmp = POSTPORT;
123       }
124       conn->pgport = strdup(tmp);
125     } else
126       conn->pgport = strdup(pgport);
127     
128     if (!pgtty || pgtty[0] == '\0') {
129       if (!(tmp = getenv("PGTTY"))) {
130         tmp = DefaultTty;
131       }
132       conn->pgtty = strdup(tmp);
133     } else
134       conn->pgtty = strdup(pgtty);
135     
136     if (!pgoptions || pgoptions[0] == '\0') {
137       if (!(tmp = getenv("PGOPTIONS"))) {
138         tmp = DefaultOption;
139       }
140       conn->pgoptions = strdup(tmp);
141     } else
142       conn->pgoptions = strdup(pgoptions);
143
144     if (tmp = getenv("PGUSER")) {
145       error = FALSE;
146       conn->pguser = strdup(tmp);
147     } else {
148       tmp = fe_getauthname(errorMessage);
149       if (tmp == 0) {
150         error = TRUE;
151         sprintf(conn->errorMessage,
152                 "FATAL: PQsetdb: Unable to determine a Postgres username!\n");
153       } else {
154         error = FALSE;
155         conn->pguser = tmp;
156       }
157     }
158
159     if (!error) {
160       if (((tmp = (char *)dbName) && (dbName[0] != '\0')) ||
161           ((tmp = getenv("PGDATABASE")))) {
162         conn->dbName = strdup(tmp);
163       } else conn->dbName = conn->pguser;
164     } else conn->dbName = NULL;
165
166     if (error) conn->status = CONNECTION_BAD;
167     else {
168       conn->status = connectDB(conn);  
169         /* Puts message in conn->errorMessage */
170       if (conn->status == CONNECTION_OK) {
171         PGresult *res;
172         /* Send a blank query to make sure everything works; 
173            in particular, that the database exists.
174            */
175         res = PQexec(conn," ");
176         if (res == NULL || res->resultStatus != PGRES_EMPTY_QUERY) {
177           /* PQexec has put error message in conn->errorMessage */
178           closePGconn(conn);
179         }
180         PQclear(res);
181       }
182     }
183   }        
184   return conn;
185 }
186
187     
188 /*
189  * connectDB -
190  * make a connection to the backend so it is ready to receive queries.  
191  * return CONNECTION_OK if successful, CONNECTION_BAD if not.
192  *
193  */
194 static ConnStatusType
195 connectDB(PGconn *conn)
196 {
197     struct hostent *hp;
198
199     StartupInfo startup;
200     PacketBuf   pacBuf;
201     int         status;
202     MsgType     msgtype;
203     int         laddrlen = sizeof(struct sockaddr);
204     Port        *port = conn->port;
205     int         portno;
206
207     /*
208     //
209     // Initialize the startup packet. 
210     //
211     // This data structure is used for the seq-packet protocol.  It
212     // describes the frontend-backend connection.
213     //
214     //
215     */
216     strncpy(startup.user,conn->pguser,sizeof(startup.user));
217     strncpy(startup.database,conn->dbName,sizeof(startup.database));
218     strncpy(startup.tty,conn->pgtty,sizeof(startup.tty));
219     if (conn->pgoptions) {
220         strncpy(startup.options,conn->pgoptions, sizeof(startup.options));
221     }
222     else
223         startup.options[0]='\0'; 
224     startup.execFile[0]='\0';  /* not used */
225
226     /*
227     //
228     // Open a connection to postmaster/backend.
229     //
230     */
231     port = (Port *) malloc(sizeof(Port));
232     memset((char *) port, 0, sizeof(Port));
233
234     if (!(hp = gethostbyname(conn->pghost)) || hp->h_addrtype != AF_INET) {
235         (void) sprintf(conn->errorMessage,
236                        "connectDB() --  unknown hostname: %s\n",
237                        conn->pghost);
238         goto connect_errReturn;
239     }
240     memset((char *) &port->raddr, 0, sizeof(port->raddr));
241     memmove((char *) &(port->raddr.sin_addr),
242             (char *) hp->h_addr, 
243             hp->h_length);
244     port->raddr.sin_family = AF_INET;
245     portno = atoi(conn->pgport);
246     port->raddr.sin_port = htons((unsigned short)(portno));
247     
248     /* connect to the server  */
249     if ((port->sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
250         (void) sprintf(conn->errorMessage,
251                "connectDB() -- socket() failed: errno=%d\n%s\n",
252                errno, strerror(errno));
253         goto connect_errReturn; 
254     }
255     if (connect(port->sock, (struct sockaddr *)&port->raddr,
256                 sizeof(port->raddr)) < 0) {
257         (void) sprintf(conn->errorMessage,
258                        "connectDB() failed: Is the postmaster running at '%s' on port '%s'?\n",
259                        conn->pghost,conn->pgport);
260         goto connect_errReturn; 
261     }
262     
263
264     /* fill in the client address */
265     if (getsockname(port->sock, (struct sockaddr *) &port->laddr,
266                     &laddrlen) < 0) {
267         (void) sprintf(conn->errorMessage,
268                "connectDB() -- getsockname() failed: errno=%d\n%s\n",
269                errno, strerror(errno));
270         goto connect_errReturn; 
271     }
272     
273     /* by this point, connection has been opened */
274     msgtype = fe_getauthsvc(conn->errorMessage);
275
276 /*    pacBuf = startup2PacketBuf(&startup);*/
277     startup2PacketBuf(&startup, &pacBuf);
278     pacBuf.msgtype = (MsgType) htonl(msgtype);
279     status = packetSend(port, &pacBuf, sizeof(PacketBuf), BLOCKING);
280     
281     if (status == STATUS_ERROR)
282         {
283         sprintf(conn->errorMessage,
284                "connectDB() --  couldn't send complete packet: errno=%d\n%s\n", errno,strerror(errno));
285         goto connect_errReturn;
286         }
287
288     /* authenticate as required*/
289     if (fe_sendauth(msgtype, port, conn->pghost, 
290                     conn->errorMessage) != STATUS_OK) {
291       (void) sprintf(conn->errorMessage,
292              "connectDB() --  authentication failed with %s\n",
293                conn->pghost);
294       goto connect_errReturn;   
295     }
296     
297     /* set up the socket file descriptors */
298     conn->Pfout = fdopen(port->sock, "w");
299     conn->Pfin = fdopen(dup(port->sock), "r");
300     if (!conn->Pfout || !conn->Pfin) {
301         (void) sprintf(conn->errorMessage,
302                "connectDB() -- fdopen() failed: errno=%d\n%s\n",
303                errno, strerror(errno));
304       goto connect_errReturn;   
305     }
306     
307     conn->port = port;
308
309     return CONNECTION_OK;
310
311 connect_errReturn:
312     return CONNECTION_BAD;
313
314 }
315
316 /*
317  * freePGconn
318  *   - free the PGconn data structure 
319  *
320  */
321 static void 
322 freePGconn(PGconn *conn)
323 {
324   if (conn->pghost) free(conn->pghost);
325   if (conn->pgtty) free(conn->pgtty);
326   if (conn->pgoptions) free(conn->pgoptions);
327   if (conn->pgport) free(conn->pgport);
328   if (conn->dbName) free(conn->dbName);
329   if (conn->pguser) free(conn->pguser);
330   if (conn->notifyList) DLFreeList(conn->notifyList);
331   free(conn);
332 }
333
334 /*
335    closePGconn
336      - properly close a connection to the backend
337 */
338 static void
339 closePGconn(PGconn *conn)
340 {
341     const struct sigaction ignore_action = {SIG_IGN, 0, 0, NULL};
342     struct sigaction oldaction;
343
344     /* If connection is already gone, that's cool.  No reason for kernel
345        to kill us when we try to write to it.  So ignore SIGPIPE signals.
346        */
347     sigaction(SIGPIPE, (struct sigaction *) &ignore_action, &oldaction);
348     fputs("X\0", conn->Pfout);
349     fflush(conn->Pfout);
350     sigaction(SIGPIPE, &oldaction, NULL);
351     if (conn->Pfout) fclose(conn->Pfout);
352     if (conn->Pfin)  fclose(conn->Pfin);
353     if (conn->Pfdebug) fclose(conn->Pfdebug);
354     conn->status = CONNECTION_BAD;  /* Well, not really _bad_ - just absent */
355 }
356
357 /*
358    PQfinish:
359       properly close a connection to the backend
360       also frees the PGconn data structure so it shouldn't be re-used 
361       after this
362 */
363 void
364 PQfinish(PGconn *conn)
365 {
366   if (!conn) {
367     fprintf(stderr,"PQfinish() -- pointer to PGconn is null");
368   } else {
369     if (conn->status == CONNECTION_OK)
370       closePGconn(conn);
371     freePGconn(conn);
372   }
373 }
374
375 /* PQreset :
376    resets the connection to the backend
377    closes the existing connection and makes a new one 
378 */
379 void
380 PQreset(PGconn *conn)
381 {
382   if (!conn) {
383     fprintf(stderr,"PQreset() -- pointer to PGconn is null");
384   } else {
385     closePGconn(conn);
386     conn->status = connectDB(conn);
387   }
388 }
389
390 /*
391  * PacketSend()
392  *
393  this is just like PacketSend(), defined in backend/libpq/pqpacket.c
394  but we define it here to avoid linking in all of libpq.a
395
396  * packetSend -- send a single-packet message.
397  *
398  * RETURNS: STATUS_ERROR if the write fails, STATUS_OK otherwise.
399  * SIDE_EFFECTS: may block.
400  * NOTES: Non-blocking writes would significantly complicate 
401  *      buffer management.  For now, we're not going to do it.
402  *
403 */
404 static int
405 packetSend(Port *port,
406            PacketBuf *buf,
407            PacketLen len,
408            bool nonBlocking)
409 {
410     PacketLen   totalLen;
411     int         addrLen = sizeof(struct sockaddr_in);
412     
413     totalLen = len;
414     
415     len = sendto(port->sock, (Addr) buf, totalLen, /* flags */ 0,
416                  (struct sockaddr *)&(port->raddr), addrLen);
417     
418     if (len < totalLen) {
419         return(STATUS_ERROR);
420     }
421     
422     return(STATUS_OK);
423 }
424
425 /*
426  * startup2PacketBuf()
427  *
428  * this is just like StartupInfo2Packet(), defined in backend/libpq/pqpacket.c
429  * but we repeat it here so we don't have to link in libpq.a
430  * 
431  * converts a StartupInfo structure to a PacketBuf
432  */
433 static void
434 startup2PacketBuf(StartupInfo* s, PacketBuf* res)
435 {
436   char* tmp;
437
438 /*  res = (PacketBuf*)malloc(sizeof(PacketBuf)); */
439   res->len = htonl(sizeof(PacketBuf));
440   /* use \n to delimit the strings */
441   res->data[0] = '\0';
442
443   tmp= res->data;
444
445   strncpy(tmp, s->database, sizeof(s->database));
446   tmp += sizeof(s->database);
447   strncpy(tmp, s->user, sizeof(s->user));
448   tmp += sizeof(s->user);
449   strncpy(tmp, s->options, sizeof(s->options));
450   tmp += sizeof(s->options);
451   strncpy(tmp, s->execFile, sizeof(s->execFile));
452   tmp += sizeof(s->execFile);
453   strncpy(tmp, s->tty, sizeof(s->execFile));
454 }
455
456 /* =========== accessor functions for PGconn ========= */
457 char* 
458 PQdb(PGconn* conn)
459 {
460   if (!conn) {
461     fprintf(stderr,"PQdb() -- pointer to PGconn is null");
462     return (char *)NULL;
463   }
464   return conn->dbName;
465 }
466
467 char* 
468 PQhost(PGconn* conn)
469 {
470   if (!conn) {
471     fprintf(stderr,"PQhost() -- pointer to PGconn is null");
472     return (char *)NULL;
473   }
474
475   return conn->pghost;
476 }
477
478 char* 
479 PQoptions(PGconn* conn)
480 {
481   if (!conn) {
482     fprintf(stderr,"PQoptions() -- pointer to PGconn is null");
483     return (char *)NULL;
484   }
485   return conn->pgoptions;
486 }
487
488 char* 
489 PQtty(PGconn* conn)
490 {
491   if (!conn) {
492     fprintf(stderr,"PQtty() -- pointer to PGconn is null");
493     return (char *)NULL;
494   }
495   return conn->pgtty;
496 }
497
498 char*
499 PQport(PGconn* conn)
500 {
501   if (!conn) {
502     fprintf(stderr,"PQport() -- pointer to PGconn is null");
503     return (char *)NULL;
504   }
505   return conn->pgport;
506 }
507
508 ConnStatusType
509 PQstatus(PGconn* conn)
510 {
511   if (!conn) {
512     fprintf(stderr,"PQstatus() -- pointer to PGconn is null");
513     return CONNECTION_BAD;
514   }
515   return conn->status;
516 }
517
518 char* 
519 PQerrorMessage(PGconn* conn)
520 {
521   if (!conn) {
522     fprintf(stderr,"PQerrorMessage() -- pointer to PGconn is null");
523     return (char *)NULL;
524   }
525   return conn->errorMessage;
526 }
527
528 void
529 PQtrace(PGconn *conn, FILE* debug_port)
530 {
531   if (conn == NULL ||
532       conn->status == CONNECTION_BAD) {
533     return;
534   }
535   PQuntrace(conn);
536   conn->Pfdebug = debug_port;
537 }
538
539 void 
540 PQuntrace(PGconn *conn)
541 {
542   if (conn == NULL ||
543       conn->status == CONNECTION_BAD) {
544     return;
545   }
546   if (conn->Pfdebug) {
547     fflush(conn->Pfdebug);
548     fclose(conn->Pfdebug);
549     conn->Pfdebug = NULL;
550   }
551 }