1 /*-------------------------------------------------------------------------
4 * functions related to setting up a connection to the backend
6 * Copyright (c) 1994, Regents of the University of California
10 * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.18 1996/11/11 12:16:54 scrappy Exp $
12 *-------------------------------------------------------------------------
16 #include <sys/types.h>
17 #include <sys/socket.h>
26 #include <ctype.h> /* for isspace() */
29 #include "libpq/pqcomm.h" /* for decls of MsgType, PacketBuf, StartupInfo */
33 #if defined(ultrix4) || defined(next)
34 /* ultrix is lame and doesn't have strdup in libc for some reason */
35 /* [TRH] So doesn't NEXTSTEP. But whaddaya expect for a non-ANSI
36 standard function? (My, my. Touchy today, are we?) */
39 strdup(const char *string)
43 if ((nstr = malloc(strlen(string)+1)) != NULL)
49 /* use a local version instead of the one found in pqpacket.c */
50 static ConnStatusType connectDB(PGconn *conn);
52 static int packetSend(Port *port, PacketBuf *buf, PacketLen len,
54 static void startup2PacketBuf(StartupInfo* s, PacketBuf* res);
55 static void freePGconn(PGconn *conn);
56 static void closePGconn(PGconn *conn);
57 static int conninfo_parse(const char *conninfo, char *errorMessage);
58 static char *conninfo_getval(char *keyword);
59 static void conninfo_free(void);
61 #define NOTIFYLIST_INITIAL_SIZE 10
62 #define NOTIFYLIST_GROWBY 10
66 * Definition of the conninfo parametes and their fallback resources.
67 * If Environment-Var and Compiled-in are specified as NULL, no
68 * fallback is available. If after all no value can be determined
69 * for an option, an error is returned.
71 * The values for dbname and user are treated special in conninfo_parse.
72 * If the Compiled-in resource is specified as a NULL value, the
73 * user is determined by fe_getauthname() and for dbname the user
76 * The Label and Disp-Char entries are provided for applications that
77 * want to use PQconndefaults() to create a generic database connection
78 * dialog. Disp-Char is defined as follows:
79 * "" Normal input field
82 static PQconninfoOption PQconninfoOptions[] = {
83 /* ----------------------------------------------------------------- */
84 /* Option-name Environment-Var Compiled-in Current value */
86 /* ----------------- --------------- --------------- --------------- */
87 { "user", "PGUSER", NULL, NULL,
88 "Database-User", "", 20 },
90 { "dbname", "PGDATABASE", NULL, NULL,
91 "Database-Name", "", 20 },
93 { "host", "PGHOST", DefaultHost, NULL,
94 "Database-Host", "", 40 },
96 { "port", "PGPORT", POSTPORT, NULL,
97 "Database-Port", "", 6 },
99 { "tty", "PGTTY", DefaultTty, NULL,
100 "Backend-Debug-TTY", "D", 40 },
102 { "options", "PGOPTIONS", DefaultOption, NULL,
103 "Backend-Debug-Options", "D", 40 },
104 /* ----------------- --------------- --------------- --------------- */
105 { NULL, NULL, NULL, NULL,
112 * establishes a connectin to a postgres backend through the postmaster
113 * using connection information in a string.
115 * The conninfo string is a list of
119 * definitions. Value might be a single value containing no whitespaces
120 * or a single quoted string. If a single quote should appear everywhere
121 * in the value, it must be escaped with a backslash like \'
123 * Returns a PGconn* which is needed for all subsequent libpq calls
124 * if the status field of the connection returned is CONNECTION_BAD,
125 * then some fields may be null'ed out instead of having valid values
129 PQconnectdb(const char *conninfo)
132 PQconninfoOption *option;
133 char errorMessage[ERROR_MSG_LENGTH];
136 * Allocate memory for the conn structure
139 conn = (PGconn*)malloc(sizeof(PGconn));
142 "FATAL: PQsetdb() -- unable to allocate memory for a PGconn");
143 return (PGconn*)NULL;
145 memset((char *)conn, 0, sizeof(PGconn));
148 * Parse the conninfo string and get the fallback resources
151 if(conninfo_parse(conninfo, errorMessage) < 0) {
152 conn->status = CONNECTION_BAD;
153 strcpy(conn->errorMessage, errorMessage);
159 * Check that we have all connection parameters
162 for(option = PQconninfoOptions; option->keyword != NULL; option++) {
163 if(option->val != NULL) continue; /* Value was in conninfo */
166 * No value was found for this option. Return an error.
169 conn->status = CONNECTION_BAD;
170 sprintf(conn->errorMessage,
171 "ERROR: PQconnectdb(): Cannot determine a value for option '%s'.\n",
173 strcat(conn->errorMessage,
174 "Option not specified in conninfo string");
175 if(option->environ) {
176 strcat(conn->errorMessage,
177 ", environment variable ");
178 strcat(conn->errorMessage, option->environ);
179 strcat(conn->errorMessage, "\nnot set");
181 strcat(conn->errorMessage, " and no compiled in default value.\n");
187 * Setup the conn structure
192 conn->Pfdebug = NULL;
194 conn->notifyList = DLNewList();
195 conn->lobjfuncs = NULL;
197 conn->pghost = strdup(conninfo_getval("host"));
198 conn->pgport = strdup(conninfo_getval("port"));
199 conn->pgtty = strdup(conninfo_getval("tty"));
200 conn->pgoptions = strdup(conninfo_getval("options"));
201 conn->pguser = strdup(conninfo_getval("user"));
202 conn->dbName = strdup(conninfo_getval("dbname"));
205 * Free the connection info - all is in conn now
211 * Connect to the database
214 conn->status = connectDB(conn);
215 if (conn->status == CONNECTION_OK) {
217 /* Send a blank query to make sure everything works; in particular, that
220 res = PQexec(conn," ");
221 if (res == NULL || res->resultStatus != PGRES_EMPTY_QUERY) {
222 /* PQexec has put error message in conn->errorMessage */
234 * Parse an empty string like PQconnectdb() would do and return the
235 * address of the connection options structure. Using this function
236 * an application might determine all possible options and their
237 * current default values.
243 char errorMessage[ERROR_MSG_LENGTH];
245 conninfo_parse("", errorMessage);
246 return PQconninfoOptions;
252 * establishes a connection to a postgres backend through the postmaster
253 * at the specified host and port.
255 * returns a PGconn* which is needed for all subsequent libpq calls
256 * if the status field of the connection returned is CONNECTION_BAD,
257 * then some fields may be null'ed out instead of having valid values
259 * Uses these environment variables:
261 * PGHOST identifies host to which to connect if <pghost> argument
262 * is NULL or a null string.
264 * PGPORT identifies TCP port to which to connect if <pgport> argument
265 * is NULL or a null string.
267 * PGTTY identifies tty to which to send messages if <pgtty> argument
268 * is NULL or a null string.
270 * PGOPTIONS identifies connection options if <pgoptions> argument is
271 * NULL or a null string.
273 * PGUSER Postgres username to associate with the connection.
275 * PGDATABASE name of database to which to connect if <pgdatabase>
276 * argument is NULL or a null string
278 * None of the above need be defined. There are defaults for all of them.
283 PQsetdb(const char *pghost, const char* pgport, const char* pgoptions, const char* pgtty, const char* dbName)
287 char errorMessage[ERROR_MSG_LENGTH];
288 /* An error message from some service we call. */
290 /* We encountered an error that prevents successful completion */
292 conn = (PGconn*)malloc(sizeof(PGconn));
296 "FATAL: PQsetdb() -- unable to allocate memory for a PGconn");
300 conn->Pfdebug = NULL;
302 conn->notifyList = DLNewList();
303 conn->lobjfuncs = NULL;
305 if (!pghost || pghost[0] == '\0') {
306 if (!(tmp = getenv("PGHOST"))) {
309 conn->pghost = strdup(tmp);
311 conn->pghost = strdup(pghost);
313 if (!pgport || pgport[0] == '\0') {
314 if (!(tmp = getenv("PGPORT"))) {
317 conn->pgport = strdup(tmp);
319 conn->pgport = strdup(pgport);
321 if (!pgtty || pgtty[0] == '\0') {
322 if (!(tmp = getenv("PGTTY"))) {
325 conn->pgtty = strdup(tmp);
327 conn->pgtty = strdup(pgtty);
329 if (!pgoptions || pgoptions[0] == '\0') {
330 if (!(tmp = getenv("PGOPTIONS"))) {
333 conn->pgoptions = strdup(tmp);
335 conn->pgoptions = strdup(pgoptions);
337 if ((tmp = getenv("PGUSER"))) {
339 conn->pguser = strdup(tmp);
341 tmp = fe_getauthname(errorMessage);
344 sprintf(conn->errorMessage,
345 "FATAL: PQsetdb: Unable to determine a Postgres username!\n");
353 if (((tmp = (char *)dbName) && (dbName[0] != '\0')) ||
354 ((tmp = getenv("PGDATABASE")))) {
355 conn->dbName = strdup(tmp);
356 } else conn->dbName = strdup(conn->pguser);
357 } else conn->dbName = NULL;
359 if (error) conn->status = CONNECTION_BAD;
361 conn->status = connectDB(conn);
362 /* Puts message in conn->errorMessage */
363 if (conn->status == CONNECTION_OK) {
365 /* Send a blank query to make sure everything works;
366 in particular, that the database exists.
368 res = PQexec(conn," ");
369 if (res == NULL || res->resultStatus != PGRES_EMPTY_QUERY) {
370 /* PQexec has put error message in conn->errorMessage */
383 * make a connection to the backend so it is ready to receive queries.
384 * return CONNECTION_OK if successful, CONNECTION_BAD if not.
387 static ConnStatusType
388 connectDB(PGconn *conn)
396 int laddrlen = sizeof(struct sockaddr);
397 Port *port = conn->port;
402 // Initialize the startup packet.
404 // This data structure is used for the seq-packet protocol. It
405 // describes the frontend-backend connection.
409 strncpy(startup.user,conn->pguser,sizeof(startup.user));
410 strncpy(startup.database,conn->dbName,sizeof(startup.database));
411 strncpy(startup.tty,conn->pgtty,sizeof(startup.tty));
412 if (conn->pgoptions) {
413 strncpy(startup.options,conn->pgoptions, sizeof(startup.options));
416 startup.options[0]='\0';
417 startup.execFile[0]='\0'; /* not used */
421 // Open a connection to postmaster/backend.
424 port = (Port *) malloc(sizeof(Port));
425 memset((char *) port, 0, sizeof(Port));
427 if (!(hp = gethostbyname(conn->pghost)) || hp->h_addrtype != AF_INET) {
428 (void) sprintf(conn->errorMessage,
429 "connectDB() -- unknown hostname: %s\n",
431 goto connect_errReturn;
433 memset((char *) &port->raddr, 0, sizeof(port->raddr));
434 memmove((char *) &(port->raddr.sin_addr),
437 port->raddr.sin_family = AF_INET;
438 portno = atoi(conn->pgport);
439 port->raddr.sin_port = htons((unsigned short)(portno));
441 /* connect to the server */
442 if ((port->sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
443 (void) sprintf(conn->errorMessage,
444 "connectDB() -- socket() failed: errno=%d\n%s\n",
445 errno, strerror(errno));
446 goto connect_errReturn;
448 if (connect(port->sock, (struct sockaddr *)&port->raddr,
449 sizeof(port->raddr)) < 0) {
450 (void) sprintf(conn->errorMessage,
451 "connectDB() failed: Is the postmaster running at '%s' on port '%s'?\n",
452 conn->pghost,conn->pgport);
453 goto connect_errReturn;
457 /* fill in the client address */
458 if (getsockname(port->sock, (struct sockaddr *) &port->laddr,
460 (void) sprintf(conn->errorMessage,
461 "connectDB() -- getsockname() failed: errno=%d\n%s\n",
462 errno, strerror(errno));
463 goto connect_errReturn;
466 /* by this point, connection has been opened */
467 msgtype = fe_getauthsvc(conn->errorMessage);
469 /* pacBuf = startup2PacketBuf(&startup);*/
470 startup2PacketBuf(&startup, &pacBuf);
471 pacBuf.msgtype = (MsgType) htonl(msgtype);
472 status = packetSend(port, &pacBuf, sizeof(PacketBuf), BLOCKING);
474 if (status == STATUS_ERROR)
476 sprintf(conn->errorMessage,
477 "connectDB() -- couldn't send complete packet: errno=%d\n%s\n", errno,strerror(errno));
478 goto connect_errReturn;
481 /* authenticate as required*/
482 if (fe_sendauth(msgtype, port, conn->pghost,
483 conn->errorMessage) != STATUS_OK) {
484 (void) sprintf(conn->errorMessage,
485 "connectDB() -- authentication failed with %s\n",
487 goto connect_errReturn;
490 /* set up the socket file descriptors */
491 conn->Pfout = fdopen(port->sock, "w");
492 conn->Pfin = fdopen(dup(port->sock), "r");
493 if (!conn->Pfout || !conn->Pfin) {
494 (void) sprintf(conn->errorMessage,
495 "connectDB() -- fdopen() failed: errno=%d\n%s\n",
496 errno, strerror(errno));
497 goto connect_errReturn;
502 return CONNECTION_OK;
505 return CONNECTION_BAD;
511 * - free the PGconn data structure
515 freePGconn(PGconn *conn)
517 if (conn->pghost) free(conn->pghost);
518 if (conn->pgtty) free(conn->pgtty);
519 if (conn->pgoptions) free(conn->pgoptions);
520 if (conn->pgport) free(conn->pgport);
521 if (conn->dbName) free(conn->dbName);
522 if (conn->pguser) free(conn->pguser);
523 if (conn->notifyList) DLFreeList(conn->notifyList);
524 if (conn->lobjfuncs) free(conn->lobjfuncs);
530 - properly close a connection to the backend
533 closePGconn(PGconn *conn)
535 struct sigaction ignore_action;
536 /* This is used as a constant, but not declared as such because the
537 sigaction structure is defined differently on different systems */
538 struct sigaction oldaction;
540 /* If connection is already gone, that's cool. No reason for kernel
541 to kill us when we try to write to it. So ignore SIGPIPE signals.
543 ignore_action.sa_handler = SIG_IGN;
544 sigemptyset(&ignore_action.sa_mask);
545 ignore_action.sa_flags = 0;
546 sigaction(SIGPIPE, (struct sigaction *) &ignore_action, &oldaction);
548 fputs("X\0", conn->Pfout);
550 sigaction(SIGPIPE, &oldaction, NULL);
551 if (conn->Pfout) fclose(conn->Pfout);
552 if (conn->Pfin) fclose(conn->Pfin);
553 if (conn->Pfdebug) fclose(conn->Pfdebug);
554 conn->status = CONNECTION_BAD; /* Well, not really _bad_ - just absent */
559 properly close a connection to the backend
560 also frees the PGconn data structure so it shouldn't be re-used
564 PQfinish(PGconn *conn)
567 fprintf(stderr,"PQfinish() -- pointer to PGconn is null");
569 if (conn->status == CONNECTION_OK)
576 resets the connection to the backend
577 closes the existing connection and makes a new one
580 PQreset(PGconn *conn)
583 fprintf(stderr,"PQreset() -- pointer to PGconn is null");
586 conn->status = connectDB(conn);
593 this is just like PacketSend(), defined in backend/libpq/pqpacket.c
594 but we define it here to avoid linking in all of libpq.a
596 * packetSend -- send a single-packet message.
598 * RETURNS: STATUS_ERROR if the write fails, STATUS_OK otherwise.
599 * SIDE_EFFECTS: may block.
600 * NOTES: Non-blocking writes would significantly complicate
601 * buffer management. For now, we're not going to do it.
605 packetSend(Port *port,
611 int addrLen = sizeof(struct sockaddr_in);
615 len = sendto(port->sock, (Addr) buf, totalLen, /* flags */ 0,
616 (struct sockaddr *)&(port->raddr), addrLen);
618 if (len < totalLen) {
619 return(STATUS_ERROR);
626 * startup2PacketBuf()
628 * this is just like StartupInfo2Packet(), defined in backend/libpq/pqpacket.c
629 * but we repeat it here so we don't have to link in libpq.a
631 * converts a StartupInfo structure to a PacketBuf
634 startup2PacketBuf(StartupInfo* s, PacketBuf* res)
638 /* res = (PacketBuf*)malloc(sizeof(PacketBuf)); */
639 res->len = htonl(sizeof(PacketBuf));
640 /* use \n to delimit the strings */
645 strncpy(tmp, s->database, sizeof(s->database));
646 tmp += sizeof(s->database);
647 strncpy(tmp, s->user, sizeof(s->user));
648 tmp += sizeof(s->user);
649 strncpy(tmp, s->options, sizeof(s->options));
650 tmp += sizeof(s->options);
651 strncpy(tmp, s->execFile, sizeof(s->execFile));
652 tmp += sizeof(s->execFile);
653 strncpy(tmp, s->tty, sizeof(s->execFile));
657 * Conninfo parser routine
660 static int conninfo_parse(const char *conninfo, char *errorMessage)
668 PQconninfoOption *option;
669 char errortmp[ERROR_MSG_LENGTH];
673 if((buf = strdup(conninfo)) == NULL) {
675 "FATAL: cannot allocate memory for copy of conninfo string\n");
681 /* Skip blanks before the parameter name */
687 /* Get the parameter name */
706 /* Check that there is a following '=' */
708 sprintf(errorMessage,
709 "ERROR: PQconnectdb() - Missing '=' after '%s' in conninfo\n",
716 /* Skip blanks after the '=' */
748 sprintf(errorMessage,
749 "ERROR: PQconnectdb() - unterminated quoted string in conninfo\n");
770 * Now we have the name and the value. Search
771 * for the param record.
774 for(option = PQconninfoOptions; option->keyword != NULL; option++) {
775 if(!strcmp(option->keyword, pname)) {
779 if(option->keyword == NULL) {
780 sprintf(errorMessage,
781 "ERROR: PQconnectdb() - unknown option '%s'\n",
791 option->val = strdup(pval);
797 * Get the fallback resources for parameters not specified
798 * in the conninfo string.
801 for(option = PQconninfoOptions; option->keyword != NULL; option++) {
802 if(option->val != NULL) continue; /* Value was in conninfo */
805 * Try to get the environment variable fallback
808 if(option->environ != NULL) {
809 if((tmp = getenv(option->environ)) != NULL) {
810 option->val = strdup(tmp);
816 * No environment variable specified or this one isn't set -
820 if(option->compiled != NULL) {
821 option->val = strdup(option->compiled);
826 * Special handling for user
829 if(!strcmp(option->keyword, "user")) {
830 tmp = fe_getauthname(errortmp);
832 option->val = strdup(tmp);
837 * Special handling for dbname
840 if(!strcmp(option->keyword, "dbname")) {
841 tmp = conninfo_getval("user");
843 option->val = strdup(tmp);
853 conninfo_getval(char *keyword)
855 PQconninfoOption *option;
857 for(option = PQconninfoOptions; option->keyword != NULL; option++) {
858 if (!strcmp(option->keyword, keyword)) {
870 PQconninfoOption *option;
872 for(option = PQconninfoOptions; option->keyword != NULL; option++) {
873 if(option->val != NULL) {
880 /* =========== accessor functions for PGconn ========= */
885 fprintf(stderr,"PQdb() -- pointer to PGconn is null");
895 fprintf(stderr,"PQuser() -- pointer to PGconn is null");
905 fprintf(stderr,"PQhost() -- pointer to PGconn is null");
913 PQoptions(PGconn* conn)
916 fprintf(stderr,"PQoptions() -- pointer to PGconn is null");
919 return conn->pgoptions;
926 fprintf(stderr,"PQtty() -- pointer to PGconn is null");
936 fprintf(stderr,"PQport() -- pointer to PGconn is null");
943 PQstatus(PGconn* conn)
946 fprintf(stderr,"PQstatus() -- pointer to PGconn is null");
947 return CONNECTION_BAD;
953 PQerrorMessage(PGconn* conn)
956 fprintf(stderr,"PQerrorMessage() -- pointer to PGconn is null");
959 return conn->errorMessage;
963 PQtrace(PGconn *conn, FILE* debug_port)
966 conn->status == CONNECTION_BAD) {
970 conn->Pfdebug = debug_port;
974 PQuntrace(PGconn *conn)
977 conn->status == CONNECTION_BAD) {
981 fflush(conn->Pfdebug);
982 fclose(conn->Pfdebug);
983 conn->Pfdebug = NULL;