fe-connect.c 12.4 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
M
Fixes:  
Marc G. Fournier 已提交
10
 *    $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.7 1996/08/19 13:25:40 scrappy Exp $
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
 *
 *-------------------------------------------------------------------------
 */

#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>
#include "libpq/pqcomm.h" /* for decls of MsgType, PacketBuf, StartupInfo */
#include "fe-auth.h"
#include "libpq-fe.h"

#if defined(PORTNAME_ultrix4) || defined(PORTNAME_next)
  /* 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 已提交
34
strdup(const char *string)
35 36 37
{
    char *nstr;

38 39
  if ((nstr = malloc(strlen(string)+1)) != NULL)
      strcpy(nstr, string);
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
    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
 * 
 * establishes a connectin to a postgres backend through the postmaster
 * 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 
 * ----------------
 */
PGconn* 
M
Marc G. Fournier 已提交
68
PQsetdb(const char *pghost, const char* pgport, const char* pgoptions, const char* pgtty, const char* dbName)
69
{
M
Fixes:  
Marc G. Fournier 已提交
70 71
  PGconn *conn;
  const char *tmp;
72

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

M
Fixes:  
Marc G. Fournier 已提交
75 76 77 78
  if (conn == NULL) 
    fprintf(stderr,
            "FATAL: PQsetdb() -- unable to allocate memory for a PGconn");
  else {
79 80 81 82 83
    conn->Pfout = NULL;
    conn->Pfin = NULL;
    conn->Pfdebug = NULL;
    conn->port = NULL;
    conn->notifyList = DLNewList();
M
Fixes:  
Marc G. Fournier 已提交
84
    
85
    if (!pghost || pghost[0] == '\0') {
M
Fixes:  
Marc G. Fournier 已提交
86 87 88 89
      if (!(tmp = getenv("PGHOST"))) {
        tmp = DefaultHost;
      }
      conn->pghost = strdup(tmp);
90
    } else
M
Fixes:  
Marc G. Fournier 已提交
91 92
      conn->pghost = strdup(pghost);
    
93
    if (!pgport || pgport[0] == '\0') {
M
Fixes:  
Marc G. Fournier 已提交
94 95 96 97
      if (!(tmp = getenv("PGPORT"))) {
        tmp = POSTPORT;
      }
      conn->pgport = strdup(tmp);
98
    } else
M
Fixes:  
Marc G. Fournier 已提交
99 100
      conn->pgport = strdup(pgport);
    
101
    if (!pgtty || pgtty[0] == '\0') {
M
Fixes:  
Marc G. Fournier 已提交
102 103 104 105
      if (!(tmp = getenv("PGTTY"))) {
        tmp = DefaultTty;
      }
      conn->pgtty = strdup(tmp);
106
    } else
M
Fixes:  
Marc G. Fournier 已提交
107 108
      conn->pgtty = strdup(pgtty);
    
109
    if (!pgoptions || pgoptions[0] == '\0') {
M
Fixes:  
Marc G. Fournier 已提交
110 111 112 113
      if (!(tmp = getenv("PGOPTIONS"))) {
        tmp = DefaultOption;
      }
      conn->pgoptions = strdup(tmp);
114
    } else
M
Fixes:  
Marc G. Fournier 已提交
115
      conn->pgoptions = strdup(pgoptions);
116
    if (((tmp = dbName) && (dbName[0] != '\0')) ||
M
Fixes:  
Marc G. Fournier 已提交
117
        ((tmp = getenv("PGDATABASE")))) {
118 119
      conn->dbName = strdup(tmp);
    } else {
120
      char errorMessage[ERROR_MSG_LENGTH];
121
      if ((tmp = fe_getauthname(errorMessage)) != 0) {
122
        conn->dbName = strdup(tmp);
M
Fixes:  
Marc G. Fournier 已提交
123
        free((char*)tmp);
124 125
      } else {
        sprintf(conn->errorMessage,
M
Fixes:  
Marc G. Fournier 已提交
126
                "FATAL: PQsetdb: Unable to determine a database name!\n");
127 128
        conn->dbName = NULL;
        return conn;
129 130
      }
    }
131
    conn->status = connectDB(conn);
M
Fixes:  
Marc G. Fournier 已提交
132 133 134 135 136 137 138 139 140 141 142 143 144 145
    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);
    }
  }        
  return conn;
146 147
}

M
Fixes:  
Marc G. Fournier 已提交
148
    
149 150
/*
 * connectDB -
M
Fixes:  
Marc G. Fournier 已提交
151 152
 * make a connection to the backend so it is ready to receive queries.  
 * return CONNECTION_OK if successful, CONNECTION_BAD if not.
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
 *
 */
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;

    char        *user;
    /*
    //
    // Initialize the startup packet. 
    //
    // This data structure is used for the seq-packet protocol.  It
    // describes the frontend-backend connection.
    //
    //
    */
    user = fe_getauthname(conn->errorMessage);
    if (!user)
	goto connect_errReturn;
    strncpy(startup.user,user,sizeof(startup.user));
182 183
    free(user);
    strncpy(startup.database,conn->dbName,sizeof(startup.database));
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 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 280 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
    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);
    pacBuf.msgtype = htonl(msgtype);
    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);
  if (conn->notifyList) DLFreeList(conn->notifyList);
  free(conn);
}

/*
   closePGconn
     - properly close a connection to the backend
*/
static void
closePGconn(PGconn *conn)
{
    fputs("X\0", conn->Pfout);
    fflush(conn->Pfout);
    if (conn->Pfout) fclose(conn->Pfout);
    if (conn->Pfin)  fclose(conn->Pfin);
    if (conn->Pfdebug) fclose(conn->Pfdebug);
M
Fixes:  
Marc G. Fournier 已提交
311
    conn->status = CONNECTION_BAD;  /* Well, not really _bad_ - just absent */
312 313 314 315 316 317 318 319 320 321 322
}

/*
   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)
{
323 324 325 326
  if (!conn) {
    fprintf(stderr,"PQfinish() -- pointer to PGconn is null");
  } else {
    if (conn->status == CONNECTION_OK)
327
      closePGconn(conn);
328 329
    freePGconn(conn);
  }
330 331 332 333 334 335 336 337 338
}

/* PQreset :
   resets the connection to the backend
   closes the existing connection and makes a new one 
*/
void
PQreset(PGconn *conn)
{
339 340 341
  if (!conn) {
    fprintf(stderr,"PQreset() -- pointer to PGconn is null");
  } else {
342 343
    closePGconn(conn);
    conn->status = connectDB(conn);
344
  }
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416
}

/*
 * 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)
{
417 418 419 420
  if (!conn) {
    fprintf(stderr,"PQdb() -- pointer to PGconn is null");
    return (char *)NULL;
  }
421 422 423 424 425 426
  return conn->dbName;
}

char* 
PQhost(PGconn* conn)
{
427 428 429 430 431
  if (!conn) {
    fprintf(stderr,"PQhost() -- pointer to PGconn is null");
    return (char *)NULL;
  }

432 433 434 435 436 437
  return conn->pghost;
}

char* 
PQoptions(PGconn* conn)
{
438 439 440 441
  if (!conn) {
    fprintf(stderr,"PQoptions() -- pointer to PGconn is null");
    return (char *)NULL;
  }
442 443 444 445 446 447
  return conn->pgoptions;
}

char* 
PQtty(PGconn* conn)
{
448 449 450 451
  if (!conn) {
    fprintf(stderr,"PQtty() -- pointer to PGconn is null");
    return (char *)NULL;
  }
452 453 454 455 456 457
  return conn->pgtty;
}

char*
PQport(PGconn* conn)
{
458 459 460 461
  if (!conn) {
    fprintf(stderr,"PQport() -- pointer to PGconn is null");
    return (char *)NULL;
  }
462 463 464 465 466 467
  return conn->pgport;
}

ConnStatusType
PQstatus(PGconn* conn)
{
468 469 470 471
  if (!conn) {
    fprintf(stderr,"PQstatus() -- pointer to PGconn is null");
    return CONNECTION_BAD;
  }
472 473 474 475 476 477
  return conn->status;
}

char* 
PQerrorMessage(PGconn* conn)
{
478 479 480 481
  if (!conn) {
    fprintf(stderr,"PQerrorMessage() -- pointer to PGconn is null");
    return (char *)NULL;
  }
482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508
  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;
  }
}