fe-connect.c 14.2 KB
Newer Older
1 2 3 4 5 6 7 8 9
/*-------------------------------------------------------------------------
 *
 * fe-connect.c--
 *    functions related to setting up a connection to the backend
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
10
 *    $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.14 1996/11/04 04:00:54 momjian Exp $
11 12 13 14 15 16 17 18 19 20 21 22 23
 *
 *-------------------------------------------------------------------------
 */

#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <netdb.h>
#include <errno.h>
24
#include <signal.h>
25 26 27 28

#include "postgres.h"
#include "libpq/pqcomm.h" /* for decls of MsgType, PacketBuf, StartupInfo */
#include "fe-auth.h"
29 30
#include "libpq-fe.h"

31
#if defined(ultrix4) || defined(next)
32 33 34 35 36
  /* ultrix is lame and doesn't have strdup in libc for some reason */
 /* [TRH] So doesn't NEXTSTEP.  But whaddaya expect for a non-ANSI  
standard function? (My, my. Touchy today, are we?) */
static
char *
M
Marc G. Fournier 已提交
37
strdup(const char *string)
38 39 40
{
    char *nstr;

41 42
  if ((nstr = malloc(strlen(string)+1)) != NULL)
      strcpy(nstr, string);
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
    return nstr;
}
#endif

/* use a local version instead of the one found in pqpacket.c */
static ConnStatusType connectDB(PGconn *conn);

static int packetSend(Port *port, PacketBuf *buf, PacketLen len,
		      bool nonBlocking);
static void startup2PacketBuf(StartupInfo* s, PacketBuf* res);
static void freePGconn(PGconn *conn);
static void closePGconn(PGconn *conn);

#define NOTIFYLIST_INITIAL_SIZE 10
#define NOTIFYLIST_GROWBY 10

/* ----------------
 *	PQsetdb
 * 
62
 * establishes a connection to a postgres backend through the postmaster
63 64 65 66 67
 * at the specified host and port.
 *
 * returns a PGconn* which is needed for all subsequent libpq calls
 * if the status field of the connection returned is CONNECTION_BAD,
 * then some fields may be null'ed out instead of having valid values 
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
 *
 *  Uses these environment variables:
 *
 *    PGHOST       identifies host to which to connect if <pghost> argument
 *                 is NULL or a null string.
 *
 *    PGPORT       identifies TCP port to which to connect if <pgport> argument
 *                 is NULL or a null string.
 *
 *    PGTTY        identifies tty to which to send messages if <pgtty> argument
 *                 is NULL or a null string.
 *
 *    PGOPTIONS    identifies connection options if <pgoptions> argument is
 *                 NULL or a null string.
 *
 *    PGUSER       Postgres username to associate with the connection.
 *
 *    PGDATABASE   name of database to which to connect if <pgdatabase> 
 *                 argument is NULL or a null string
 *
 *    None of the above need be defined.  There are defaults for all of them.
 *
90 91 92
 * ----------------
 */
PGconn* 
M
Marc G. Fournier 已提交
93
PQsetdb(const char *pghost, const char* pgport, const char* pgoptions, const char* pgtty, const char* dbName)
94
{
M
Fixes:  
Marc G. Fournier 已提交
95
  PGconn *conn;
96 97 98 99 100
  char *tmp;
  char errorMessage[ERROR_MSG_LENGTH];
    /* An error message from some service we call. */
  bool error;   
    /* We encountered an error that prevents successful completion */
101

M
Fixes:  
Marc G. Fournier 已提交
102
  conn = (PGconn*)malloc(sizeof(PGconn));
103

M
Fixes:  
Marc G. Fournier 已提交
104 105 106 107
  if (conn == NULL) 
    fprintf(stderr,
            "FATAL: PQsetdb() -- unable to allocate memory for a PGconn");
  else {
108 109 110 111 112
    conn->Pfout = NULL;
    conn->Pfin = NULL;
    conn->Pfdebug = NULL;
    conn->port = NULL;
    conn->notifyList = DLNewList();
M
Fixes:  
Marc G. Fournier 已提交
113
    
114
    if (!pghost || pghost[0] == '\0') {
M
Fixes:  
Marc G. Fournier 已提交
115 116 117 118
      if (!(tmp = getenv("PGHOST"))) {
        tmp = DefaultHost;
      }
      conn->pghost = strdup(tmp);
119
    } else
M
Fixes:  
Marc G. Fournier 已提交
120 121
      conn->pghost = strdup(pghost);
    
122
    if (!pgport || pgport[0] == '\0') {
M
Fixes:  
Marc G. Fournier 已提交
123 124 125 126
      if (!(tmp = getenv("PGPORT"))) {
        tmp = POSTPORT;
      }
      conn->pgport = strdup(tmp);
127
    } else
M
Fixes:  
Marc G. Fournier 已提交
128 129
      conn->pgport = strdup(pgport);
    
130
    if (!pgtty || pgtty[0] == '\0') {
M
Fixes:  
Marc G. Fournier 已提交
131 132 133 134
      if (!(tmp = getenv("PGTTY"))) {
        tmp = DefaultTty;
      }
      conn->pgtty = strdup(tmp);
135
    } else
M
Fixes:  
Marc G. Fournier 已提交
136 137
      conn->pgtty = strdup(pgtty);
    
138
    if (!pgoptions || pgoptions[0] == '\0') {
M
Fixes:  
Marc G. Fournier 已提交
139 140 141 142
      if (!(tmp = getenv("PGOPTIONS"))) {
        tmp = DefaultOption;
      }
      conn->pgoptions = strdup(tmp);
143
    } else
M
Fixes:  
Marc G. Fournier 已提交
144
      conn->pgoptions = strdup(pgoptions);
145

146
    if ((tmp = getenv("PGUSER"))) {
147 148
      error = FALSE;
      conn->pguser = strdup(tmp);
149
    } else {
150 151 152
      tmp = fe_getauthname(errorMessage);
      if (tmp == 0) {
        error = TRUE;
153
        sprintf(conn->errorMessage,
154 155 156 157
                "FATAL: PQsetdb: Unable to determine a Postgres username!\n");
      } else {
        error = FALSE;
        conn->pguser = tmp;
158 159
      }
    }
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182

    if (!error) {
      if (((tmp = (char *)dbName) && (dbName[0] != '\0')) ||
          ((tmp = getenv("PGDATABASE")))) {
        conn->dbName = strdup(tmp);
      } else conn->dbName = conn->pguser;
    } else conn->dbName = NULL;

    if (error) conn->status = CONNECTION_BAD;
    else {
      conn->status = connectDB(conn);  
        /* Puts message in conn->errorMessage */
      if (conn->status == CONNECTION_OK) {
        PGresult *res;
        /* Send a blank query to make sure everything works; 
           in particular, that the database exists.
           */
        res = PQexec(conn," ");
        if (res == NULL || res->resultStatus != PGRES_EMPTY_QUERY) {
          /* PQexec has put error message in conn->errorMessage */
          closePGconn(conn);
        }
        PQclear(res);
M
Fixes:  
Marc G. Fournier 已提交
183 184 185 186
      }
    }
  }        
  return conn;
187 188
}

M
Fixes:  
Marc G. Fournier 已提交
189
    
190 191
/*
 * connectDB -
M
Fixes:  
Marc G. Fournier 已提交
192 193
 * make a connection to the backend so it is ready to receive queries.  
 * return CONNECTION_OK if successful, CONNECTION_BAD if not.
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
 *
 */
static ConnStatusType
connectDB(PGconn *conn)
{
    struct hostent *hp;

    StartupInfo startup;
    PacketBuf   pacBuf;
    int		status;
    MsgType	msgtype;
    int         laddrlen = sizeof(struct sockaddr);
    Port        *port = conn->port;
    int         portno;

    /*
    //
    // Initialize the startup packet. 
    //
    // This data structure is used for the seq-packet protocol.  It
    // describes the frontend-backend connection.
    //
    //
    */
218
    strncpy(startup.user,conn->pguser,sizeof(startup.user));
219
    strncpy(startup.database,conn->dbName,sizeof(startup.database));
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
    strncpy(startup.tty,conn->pgtty,sizeof(startup.tty));
    if (conn->pgoptions) {
	strncpy(startup.options,conn->pgoptions, sizeof(startup.options));
    }
    else
	startup.options[0]='\0'; 
    startup.execFile[0]='\0';  /* not used */

    /*
    //
    // Open a connection to postmaster/backend.
    //
    */
    port = (Port *) malloc(sizeof(Port));
    memset((char *) port, 0, sizeof(Port));

    if (!(hp = gethostbyname(conn->pghost)) || hp->h_addrtype != AF_INET) {
	(void) sprintf(conn->errorMessage,
		       "connectDB() --  unknown hostname: %s\n",
		       conn->pghost);
	goto connect_errReturn;
    }
    memset((char *) &port->raddr, 0, sizeof(port->raddr));
    memmove((char *) &(port->raddr.sin_addr),
	    (char *) hp->h_addr, 
	    hp->h_length);
    port->raddr.sin_family = AF_INET;
    portno = atoi(conn->pgport);
    port->raddr.sin_port = htons((unsigned short)(portno));
    
    /* connect to the server  */
    if ((port->sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
	(void) sprintf(conn->errorMessage,
	       "connectDB() -- socket() failed: errno=%d\n%s\n",
	       errno, strerror(errno));
	goto connect_errReturn;	
    }
    if (connect(port->sock, (struct sockaddr *)&port->raddr,
		sizeof(port->raddr)) < 0) {
	(void) sprintf(conn->errorMessage,
		       "connectDB() failed: Is the postmaster running at '%s' on port '%s'?\n",
		       conn->pghost,conn->pgport);
	goto connect_errReturn;	
    }
    

    /* fill in the client address */
    if (getsockname(port->sock, (struct sockaddr *) &port->laddr,
		    &laddrlen) < 0) {
	(void) sprintf(conn->errorMessage,
	       "connectDB() -- getsockname() failed: errno=%d\n%s\n",
	       errno, strerror(errno));
	goto connect_errReturn;	
    }
    
    /* by this point, connection has been opened */
    msgtype = fe_getauthsvc(conn->errorMessage);

/*    pacBuf = startup2PacketBuf(&startup);*/
    startup2PacketBuf(&startup, &pacBuf);
280
    pacBuf.msgtype = (MsgType) htonl(msgtype);
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
    status = packetSend(port, &pacBuf, sizeof(PacketBuf), BLOCKING);
    
    if (status == STATUS_ERROR)
	{
	sprintf(conn->errorMessage,
	       "connectDB() --  couldn't send complete packet: errno=%d\n%s\n", errno,strerror(errno));
	goto connect_errReturn;
	}

    /* authenticate as required*/
    if (fe_sendauth(msgtype, port, conn->pghost, 
		    conn->errorMessage) != STATUS_OK) {
      (void) sprintf(conn->errorMessage,
	     "connectDB() --  authentication failed with %s\n",
	       conn->pghost);
      goto connect_errReturn;	
    }
    
    /* set up the socket file descriptors */
    conn->Pfout = fdopen(port->sock, "w");
    conn->Pfin = fdopen(dup(port->sock), "r");
    if (!conn->Pfout || !conn->Pfin) {
	(void) sprintf(conn->errorMessage,
	       "connectDB() -- fdopen() failed: errno=%d\n%s\n",
	       errno, strerror(errno));
      goto connect_errReturn;	
    }
    
    conn->port = port;

    return CONNECTION_OK;

connect_errReturn:
    return CONNECTION_BAD;

}

/*
 * freePGconn
 *   - free the PGconn data structure 
 *
 */
static void 
freePGconn(PGconn *conn)
{
  if (conn->pghost) free(conn->pghost);
  if (conn->pgtty) free(conn->pgtty);
  if (conn->pgoptions) free(conn->pgoptions);
  if (conn->pgport) free(conn->pgport);
  if (conn->dbName) free(conn->dbName);
331
  if (conn->pguser) free(conn->pguser);
332 333 334 335 336 337 338 339 340 341 342
  if (conn->notifyList) DLFreeList(conn->notifyList);
  free(conn);
}

/*
   closePGconn
     - properly close a connection to the backend
*/
static void
closePGconn(PGconn *conn)
{
343
    struct sigaction ignore_action;
344 345
      /* This is used as a constant, but not declared as such because the
         sigaction structure is defined differently on different systems */
346 347 348 349 350
    struct sigaction oldaction;

    /* If connection is already gone, that's cool.  No reason for kernel
       to kill us when we try to write to it.  So ignore SIGPIPE signals.
       */
351
    ignore_action.sa_handler = SIG_IGN;
352
    sigemptyset(&ignore_action.sa_mask);
353
    ignore_action.sa_flags = 0;
354
    sigaction(SIGPIPE, (struct sigaction *) &ignore_action, &oldaction);
355

356 357
    fputs("X\0", conn->Pfout);
    fflush(conn->Pfout);
358
    sigaction(SIGPIPE, &oldaction, NULL);
359 360 361
    if (conn->Pfout) fclose(conn->Pfout);
    if (conn->Pfin)  fclose(conn->Pfin);
    if (conn->Pfdebug) fclose(conn->Pfdebug);
M
Fixes:  
Marc G. Fournier 已提交
362
    conn->status = CONNECTION_BAD;  /* Well, not really _bad_ - just absent */
363 364 365 366 367 368 369 370 371 372 373
}

/*
   PQfinish:
      properly close a connection to the backend
      also frees the PGconn data structure so it shouldn't be re-used 
      after this
*/
void
PQfinish(PGconn *conn)
{
374 375 376 377
  if (!conn) {
    fprintf(stderr,"PQfinish() -- pointer to PGconn is null");
  } else {
    if (conn->status == CONNECTION_OK)
378
      closePGconn(conn);
379 380
    freePGconn(conn);
  }
381 382 383 384 385 386 387 388 389
}

/* PQreset :
   resets the connection to the backend
   closes the existing connection and makes a new one 
*/
void
PQreset(PGconn *conn)
{
390 391 392
  if (!conn) {
    fprintf(stderr,"PQreset() -- pointer to PGconn is null");
  } else {
393 394
    closePGconn(conn);
    conn->status = connectDB(conn);
395
  }
396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467
}

/*
 * PacketSend()
 *
 this is just like PacketSend(), defined in backend/libpq/pqpacket.c
 but we define it here to avoid linking in all of libpq.a

 * packetSend -- send a single-packet message.
 *
 * RETURNS: STATUS_ERROR if the write fails, STATUS_OK otherwise.
 * SIDE_EFFECTS: may block.
 * NOTES: Non-blocking writes would significantly complicate 
 *	buffer management.  For now, we're not going to do it.
 *
*/
static int
packetSend(Port *port,
	   PacketBuf *buf,
	   PacketLen len,
	   bool nonBlocking)
{
    PacketLen	totalLen;
    int		addrLen = sizeof(struct sockaddr_in);
    
    totalLen = len;
    
    len = sendto(port->sock, (Addr) buf, totalLen, /* flags */ 0,
		 (struct sockaddr *)&(port->raddr), addrLen);
    
    if (len < totalLen) {
	return(STATUS_ERROR);
    }
    
    return(STATUS_OK);
}

/*
 * startup2PacketBuf()
 *
 * this is just like StartupInfo2Packet(), defined in backend/libpq/pqpacket.c
 * but we repeat it here so we don't have to link in libpq.a
 * 
 * converts a StartupInfo structure to a PacketBuf
 */
static void
startup2PacketBuf(StartupInfo* s, PacketBuf* res)
{
  char* tmp;

/*  res = (PacketBuf*)malloc(sizeof(PacketBuf)); */
  res->len = htonl(sizeof(PacketBuf));
  /* use \n to delimit the strings */
  res->data[0] = '\0';

  tmp= res->data;

  strncpy(tmp, s->database, sizeof(s->database));
  tmp += sizeof(s->database);
  strncpy(tmp, s->user, sizeof(s->user));
  tmp += sizeof(s->user);
  strncpy(tmp, s->options, sizeof(s->options));
  tmp += sizeof(s->options);
  strncpy(tmp, s->execFile, sizeof(s->execFile));
  tmp += sizeof(s->execFile);
  strncpy(tmp, s->tty, sizeof(s->execFile));
}

/* =========== accessor functions for PGconn ========= */
char* 
PQdb(PGconn* conn)
{
468 469 470 471
  if (!conn) {
    fprintf(stderr,"PQdb() -- pointer to PGconn is null");
    return (char *)NULL;
  }
472 473 474 475 476 477
  return conn->dbName;
}

char* 
PQhost(PGconn* conn)
{
478 479 480 481 482
  if (!conn) {
    fprintf(stderr,"PQhost() -- pointer to PGconn is null");
    return (char *)NULL;
  }

483 484 485 486 487 488
  return conn->pghost;
}

char* 
PQoptions(PGconn* conn)
{
489 490 491 492
  if (!conn) {
    fprintf(stderr,"PQoptions() -- pointer to PGconn is null");
    return (char *)NULL;
  }
493 494 495 496 497 498
  return conn->pgoptions;
}

char* 
PQtty(PGconn* conn)
{
499 500 501 502
  if (!conn) {
    fprintf(stderr,"PQtty() -- pointer to PGconn is null");
    return (char *)NULL;
  }
503 504 505 506 507 508
  return conn->pgtty;
}

char*
PQport(PGconn* conn)
{
509 510 511 512
  if (!conn) {
    fprintf(stderr,"PQport() -- pointer to PGconn is null");
    return (char *)NULL;
  }
513 514 515 516 517 518
  return conn->pgport;
}

ConnStatusType
PQstatus(PGconn* conn)
{
519 520 521 522
  if (!conn) {
    fprintf(stderr,"PQstatus() -- pointer to PGconn is null");
    return CONNECTION_BAD;
  }
523 524 525 526 527 528
  return conn->status;
}

char* 
PQerrorMessage(PGconn* conn)
{
529 530 531 532
  if (!conn) {
    fprintf(stderr,"PQerrorMessage() -- pointer to PGconn is null");
    return (char *)NULL;
  }
533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559
  return conn->errorMessage;
}

void
PQtrace(PGconn *conn, FILE* debug_port)
{
  if (conn == NULL ||
      conn->status == CONNECTION_BAD) {
    return;
  }
  PQuntrace(conn);
  conn->Pfdebug = debug_port;
}

void 
PQuntrace(PGconn *conn)
{
  if (conn == NULL ||
      conn->status == CONNECTION_BAD) {
    return;
  }
  if (conn->Pfdebug) {
    fflush(conn->Pfdebug);
    fclose(conn->Pfdebug);
    conn->Pfdebug = NULL;
  }
}