Esempio n. 1
0
int
cfgetc(cfp *fp)
{
	int			ret;

#ifdef HAVE_LIBZ
	if (fp->compressedfp)
	{
		ret = gzgetc(fp->compressedfp);
		if (ret == EOF)
		{
			if (!gzeof(fp->compressedfp))
				exit_horribly(modulename,
					"could not read from input file: %s\n", strerror(errno));
			else
				exit_horribly(modulename,
							"could not read from input file: end of file\n");
		}
	}
	else
#endif
	{
		ret = fgetc(fp->uncompressedfp);
		if (ret == EOF)
			READ_ERROR_EXIT(fp->uncompressedfp);
	}

	return ret;
}
Esempio n. 2
0
static void
_check_database_version(ArchiveHandle *AH)
{
	const char *remoteversion_str;
	int			remoteversion;

	remoteversion_str = PQparameterStatus(AH->connection, "server_version");
	remoteversion = PQserverVersion(AH->connection);
	if (remoteversion == 0 || !remoteversion_str)
		exit_horribly(modulename, "could not get server_version from libpq\n");

	AH->public__.remoteVersionStr = pg_strdup(remoteversion_str);
	AH->public__.remoteVersion = remoteversion;
	if (!AH->archiveRemoteVersion)
		AH->archiveRemoteVersion = AH->public__.remoteVersionStr;

	if (remoteversion != PG_VERSION_NUM
		&& (remoteversion < AH->public__.minRemoteVersion ||
			remoteversion > AH->public__.maxRemoteVersion))
	{
		write_msg(NULL, "server version: %s; %s version: %s\n",
				  remoteversion_str, progname, PG_VERSION);
		exit_horribly(NULL, "aborting because of server version mismatch\n");
	}
}
Esempio n. 3
0
char *
pg_strdup(const char *string)
{
	char	   *tmp;

	if (!string)
		exit_horribly(NULL, "cannot duplicate null pointer\n");
	tmp = strdup(string);
	if (!tmp)
		exit_horribly(NULL, "out of memory\n");
	return tmp;
}
Esempio n. 4
0
/* Allocate a new compressor */
CompressorState *
AllocateCompressor(int compression, WriteFunc writeF)
{
	CompressorState *cs;
	CompressionAlgorithm alg;
	int			level;

	ParseCompressionOption(compression, &alg, &level);

#ifndef HAVE_LIBZ
	if (alg == COMPR_ALG_LIBZ)
		exit_horribly(modulename, "not built with zlib support\n");
#endif

	cs = (CompressorState *) pg_malloc0(sizeof(CompressorState));
	cs->writeF = writeF;
	cs->comprAlg = alg;

	/*
	 * Perform compression algorithm specific initialization.
	 */
#ifdef HAVE_LIBZ
	if (alg == COMPR_ALG_LIBZ)
		InitCompressorZlib(cs, level);
#endif

	return cs;
}
Esempio n. 5
0
static void
InitCompressorZlib(CompressorState *cs, int level)
{
	z_streamp	zp;

	zp = cs->zp = (z_streamp) pg_malloc(sizeof(z_stream));
	zp->zalloc = Z_NULL;
	zp->zfree = Z_NULL;
	zp->opaque = Z_NULL;

	/*
	 * zlibOutSize is the buffer size we tell zlib it can output to.  We
	 * actually allocate one extra byte because some routines want to append a
	 * trailing zero byte to the zlib output.
	 */
	cs->zlibOut = (char *) pg_malloc(ZLIB_OUT_SIZE + 1);
	cs->zlibOutSize = ZLIB_OUT_SIZE;

	if (deflateInit(zp, level) != Z_OK)
		exit_horribly(modulename,
					  "could not initialize compression library: %s\n",
					  zp->msg);

	/* Just be paranoid - maybe End is called after Start, with no Write */
	zp->next_out = (void *) cs->zlibOut;
	zp->avail_out = cs->zlibOutSize;
}
Esempio n. 6
0
int
cfread(void *ptr, int size, cfp *fp)
{
	int			ret;

	if (size == 0)
		return 0;

#ifdef HAVE_LIBZ
	if (fp->compressedfp)
	{
		ret = gzread(fp->compressedfp, ptr, size);
		if (ret != size && !gzeof(fp->compressedfp))
			exit_horribly(modulename,
					"could not read from input file: %s\n", strerror(errno));
	}
	else
#endif
	{
		ret = fread(ptr, 1, size, fp->uncompressedfp);
		if (ret != size && !feof(fp->uncompressedfp))
			READ_ERROR_EXIT(fp->uncompressedfp);
	}
	return ret;
}
Esempio n. 7
0
/*
 * Opens file 'path' in 'mode'. If 'compression' is non-zero, the file
 * is opened with libz gzopen(), otherwise with plain fopen()
 */
cfp *
cfopen(const char *path, const char *mode, int compression)
{
	cfp		   *fp = pg_malloc(sizeof(cfp));

	if (compression != 0)
	{
#ifdef HAVE_LIBZ
		fp->compressedfp = gzopen(path, mode);
		fp->uncompressedfp = NULL;
		if (fp->compressedfp == NULL)
		{
			free(fp);
			fp = NULL;
		}
#else
		exit_horribly(modulename, "not built with zlib support\n");
#endif
	}
	else
	{
#ifdef HAVE_LIBZ
		fp->compressedfp = NULL;
#endif
		fp->uncompressedfp = fopen(path, mode);
		if (fp->uncompressedfp == NULL)
		{
			free(fp);
			fp = NULL;
		}
	}

	return fp;
}
Esempio n. 8
0
/* Like exit_horribly(), but with a complaint about a particular query. */
static void
die_on_query_failure(ArchiveHandle *AH, const char *modulename, const char *query)
{
	write_msg(modulename, "query failed: %s",
			  PQerrorMessage(AH->connection));
	exit_horribly(modulename, "query was: %s\n", query);
}
Esempio n. 9
0
/* Register a callback to be run when exit_nicely is invoked. */
void
on_exit_nicely(on_exit_nicely_callback function, void *arg)
{
	if (on_exit_nicely_index >= MAX_ON_EXIT_NICELY)
		exit_horribly(NULL, "out of on_exit_nicely slots");
	on_exit_nicely_list[on_exit_nicely_index].function = function;
	on_exit_nicely_list[on_exit_nicely_index].arg = arg;
	on_exit_nicely_index++;
}
Esempio n. 10
0
static void
DeflateCompressorZlib(ArchiveHandle *AH, CompressorState *cs, bool flush)
{
	z_streamp	zp = cs->zp;
	char	   *out = cs->zlibOut;
	int			res = Z_OK;

	while (cs->zp->avail_in != 0 || flush)
	{
		res = deflate(zp, flush ? Z_FINISH : Z_NO_FLUSH);
		if (res == Z_STREAM_ERROR)
			exit_horribly(modulename,
						  "could not compress data: %s\n", zp->msg);
		if ((flush && (zp->avail_out < cs->zlibOutSize))
			|| (zp->avail_out == 0)
			|| (zp->avail_in != 0)
			)
		{
			/*
			 * Extra paranoia: avoid zero-length chunks, since a zero length
			 * chunk is the EOF marker in the custom format. This should never
			 * happen but...
			 */
			if (zp->avail_out < cs->zlibOutSize)
			{
				/*
				 * Any write function shoud do its own error checking but to
				 * make sure we do a check here as well...
				 */
				size_t		len = cs->zlibOutSize - zp->avail_out;

				if (cs->writeF(AH, out, len) != len)
					exit_horribly(modulename,
								  "could not write to output file: %s\n",
								  strerror(errno));
			}
			zp->next_out = (void *) out;
			zp->avail_out = cs->zlibOutSize;
		}

		if (res == Z_STREAM_END)
			break;
	}
}
Esempio n. 11
0
void *
pg_calloc(size_t nmemb, size_t size)
{
	void	   *tmp;

	tmp = calloc(nmemb, size);
	if (!tmp)
		exit_horribly(NULL, "out of memory\n");
	return tmp;
}
Esempio n. 12
0
void *
pg_malloc(size_t size)
{
	void	   *tmp;

	/* Avoid unportable behavior of malloc(0) */
	if (size == 0)
		size = 1;
	tmp = malloc(size);
	if (!tmp)
		exit_horribly(NULL, "out of memory\n");
	return tmp;
}
Esempio n. 13
0
void *
pg_realloc(void *ptr, size_t size)
{
	void	   *tmp;

	/* Avoid unportable behavior of realloc(NULL, 0) */
	if (ptr == NULL && size == 0)
		size = 1;
	tmp = realloc(ptr, size);
	if (!tmp)
		exit_horribly(NULL, "out of memory\n");
	return tmp;
}
Esempio n. 14
0
/*
 * Implement ahwrite() for direct-to-DB restore
 */
int
ExecuteSqlCommandBuf(Archive *AHX, const char *buf, size_t bufLen)
{
	ArchiveHandle *AH = (ArchiveHandle *) AHX;

	if (AH->outputKind == OUTPUT_COPYDATA)
	{
		/*
		 * COPY data.
		 *
		 * We drop the data on the floor if libpq has failed to enter COPY
		 * mode; this allows us to behave reasonably when trying to continue
		 * after an error in a COPY command.
		 */
		if (AH->pgCopyIn &&
			PQputCopyData(AH->connection, buf, bufLen) <= 0)
			exit_horribly(modulename, "error returned by PQputCopyData: %s",
						  PQerrorMessage(AH->connection));
	}
	else if (AH->outputKind == OUTPUT_OTHERDATA)
	{
		/*
		 * Table data expressed as INSERT commands; or, in old dump files,
		 * BLOB COMMENTS data (which is expressed as COMMENT ON commands).
		 */
		ExecuteSimpleCommands(AH, buf, bufLen);
	}
	else
	{
		/*
		 * General SQL commands; we assume that commands will not be split
		 * across calls.
		 *
		 * In most cases the data passed to us will be a null-terminated
		 * string, but if it's not, we have to add a trailing null.
		 */
		if (buf[bufLen] == '\0')
			ExecuteSqlCommand(AH, buf, "could not execute query");
		else
		{
			char	   *str = (char *) pg_malloc(bufLen + 1);

			memcpy(str, buf, bufLen);
			str[bufLen] = '\0';
			ExecuteSqlCommand(AH, str, "could not execute query");
			free(str);
		}
	}

	return bufLen;
}
Esempio n. 15
0
static size_t
WriteDataToArchiveNone(ArchiveHandle *AH, CompressorState *cs,
					   const char *data, size_t dLen)
{
	/*
	 * Any write function should do its own error checking but to make sure we
	 * do a check here as well...
	 */
	if (cs->writeF(AH, data, dLen) != dLen)
		exit_horribly(modulename,
					  "could not write to output file: %s\n",
					  strerror(errno));
	return dLen;
}
Esempio n. 16
0
/*
 * Opens file 'path' in 'mode'. If 'compression' is non-zero, the file
 * is opened with libz gzopen(), otherwise with plain fopen().
 *
 * On failure, return NULL with an error code in errno.
 */
cfp *
cfopen(const char *path, const char *mode, int compression)
{
	cfp		   *fp = pg_malloc(sizeof(cfp));

	if (compression != 0)
	{
#ifdef HAVE_LIBZ
		if (compression != Z_DEFAULT_COMPRESSION)
		{
			/* user has specified a compression level, so tell zlib to use it */
			char		mode_compression[32];

			snprintf(mode_compression, sizeof(mode_compression), "%s%d",
					 mode, compression);
			fp->compressedfp = gzopen(path, mode_compression);
		}
		else
		{
			/* don't specify a level, just use the zlib default */
			fp->compressedfp = gzopen(path, mode);
		}

		fp->uncompressedfp = NULL;
		if (fp->compressedfp == NULL)
		{
			free_keep_errno(fp);
			fp = NULL;
		}
#else
		exit_horribly(modulename, "not built with zlib support\n");
#endif
	}
	else
	{
#ifdef HAVE_LIBZ
		fp->compressedfp = NULL;
#endif
		fp->uncompressedfp = fopen(path, mode);
		if (fp->uncompressedfp == NULL)
		{
			free_keep_errno(fp);
			fp = NULL;
		}
	}

	return fp;
}
Esempio n. 17
0
/*
 * Compress and write data to the output stream (via writeF).
 */
size_t
WriteDataToArchive(ArchiveHandle *AH, CompressorState *cs,
				   const void *data, size_t dLen)
{
	switch (cs->comprAlg)
	{
		case COMPR_ALG_LIBZ:
#ifdef HAVE_LIBZ
			return WriteDataToArchiveZlib(AH, cs, data, dLen);
#else
			exit_horribly(modulename, "not built with zlib support\n");
#endif
		case COMPR_ALG_NONE:
			return WriteDataToArchiveNone(AH, cs, data, dLen);
	}
	return 0;					/* keep compiler quiet */
}
Esempio n. 18
0
/*
 * Read all compressed data from the input stream (via readF) and print it
 * out with ahwrite().
 */
void
ReadDataFromArchive(ArchiveHandle *AH, int compression, ReadFunc readF)
{
	CompressionAlgorithm alg;

	ParseCompressionOption(compression, &alg, NULL);

	if (alg == COMPR_ALG_NONE)
		ReadDataFromArchiveNone(AH, readF);
	if (alg == COMPR_ALG_LIBZ)
	{
#ifdef HAVE_LIBZ
		ReadDataFromArchiveZlib(AH, readF);
#else
		exit_horribly(modulename, "not built with zlib support\n");
#endif
	}
}
Esempio n. 19
0
static void
EndCompressorZlib(ArchiveHandle *AH, CompressorState *cs)
{
	z_streamp	zp = cs->zp;

	zp->next_in = NULL;
	zp->avail_in = 0;

	/* Flush any remaining data from zlib buffer */
	DeflateCompressorZlib(AH, cs, true);

	if (deflateEnd(zp) != Z_OK)
		exit_horribly(modulename,
					  "could not close compression stream: %s\n", zp->msg);

	free(cs->zlibOut);
	free(cs->zp);
}
Esempio n. 20
0
/*
 * Interprets a numeric 'compression' value. The algorithm implied by the
 * value (zlib or none at the moment), is returned in *alg, and the
 * zlib compression level in *level.
 */
static void
ParseCompressionOption(int compression, CompressionAlgorithm *alg, int *level)
{
	if (compression == Z_DEFAULT_COMPRESSION ||
		(compression > 0 && compression <= 9))
		*alg = COMPR_ALG_LIBZ;
	else if (compression == 0)
		*alg = COMPR_ALG_NONE;
	else
	{
		exit_horribly(modulename, "invalid compression code: %d\n",
					  compression);
		*alg = COMPR_ALG_NONE;	/* keep compiler quiet */
	}

	/* The level is just the passed-in value. */
	if (level)
		*level = compression;
}
Esempio n. 21
0
/*
 * Compress and write data to the output stream (via writeF).
 */
void
WriteDataToArchive(ArchiveHandle *AH, CompressorState *cs,
				   const void *data, size_t dLen)
{
	/* Are we aborting? */
	checkAborting(AH);

	switch (cs->comprAlg)
	{
		case COMPR_ALG_LIBZ:
#ifdef HAVE_LIBZ
			WriteDataToArchiveZlib(AH, cs, data, dLen);
#else
			exit_horribly(modulename, "not built with zlib support\n");
#endif
			break;
		case COMPR_ALG_NONE:
			WriteDataToArchiveNone(AH, cs, data, dLen);
			break;
	}
	return;
}
Esempio n. 22
0
/*
 * Open a file for writing. 'path' indicates the path name, and 'mode' must
 * be a filemode as accepted by fopen() and gzopen() that indicates writing
 * ("w", "wb", "a", or "ab").
 *
 * If 'compression' is non-zero, a gzip compressed stream is opened, and
 * and 'compression' indicates the compression level used. The ".gz" suffix
 * is automatically added to 'path' in that case.
 */
cfp *
cfopen_write(const char *path, const char *mode, int compression)
{
	cfp		   *fp;

	if (compression == 0)
		fp = cfopen(path, mode, 0);
	else
	{
#ifdef HAVE_LIBZ
		char	   *fname;

		fname = psprintf("%s.gz", path);
		fp = cfopen(fname, mode, 1);
		free(fname);
#else
		exit_horribly(modulename, "not built with zlib support\n");
		fp = NULL;				/* keep compiler quiet */
#endif
	}
	return fp;
}
Esempio n. 23
0
/*
 * Terminate a COPY operation during direct-to-DB restore
 */
void
EndDBCopyMode(Archive *AHX, const char *tocEntryTag)
{
	ArchiveHandle *AH = (ArchiveHandle *) AHX;

	if (AH->pgCopyIn)
	{
		PGresult   *res;

		if (PQputCopyEnd(AH->connection, NULL) <= 0)
			exit_horribly(modulename, "error returned by PQputCopyEnd: %s",
						  PQerrorMessage(AH->connection));

		/* Check command status and return to normal libpq state */
		res = PQgetResult(AH->connection);
		if (PQresultStatus(res) != PGRES_COMMAND_OK)
			warn_or_exit_horribly(AH, modulename, "COPY failed for table \"%s\": %s",
								tocEntryTag, PQerrorMessage(AH->connection));
		PQclear(res);

		AH->pgCopyIn = false;
	}
}
Esempio n. 24
0
/*
 * Open a file for writing. 'path' indicates the path name, and 'mode' must
 * be a filemode as accepted by fopen() and gzopen() that indicates writing
 * ("w", "wb", "a", or "ab").
 *
 * If 'compression' is non-zero, a gzip compressed stream is opened, and
 * and 'compression' indicates the compression level used. The ".gz" suffix
 * is automatically added to 'path' in that case.
 */
cfp *
cfopen_write(const char *path, const char *mode, int compression)
{
	cfp		   *fp;

	if (compression == 0)
		fp = cfopen(path, mode, 0);
	else
	{
#ifdef HAVE_LIBZ
		int			fnamelen = strlen(path) + 4;
		char	   *fname = pg_malloc(fnamelen);

		snprintf(fname, fnamelen, "%s%s", path, ".gz");
		fp = cfopen(fname, mode, 1);
		free(fname);
#else
		exit_horribly(modulename, "not built with zlib support\n");
		fp = NULL;				/* keep compiler quiet */
#endif
	}
	return fp;
}
Esempio n. 25
0
int
main(int argc, char **argv)
{
	PQExpBuffer valueBuf = NULL;
	PQExpBuffer escapeBuf = createPQExpBuffer();
	RestoreOptions *opts;
	int			c;
	int			exit_code = 0;
	Archive    *AH;
	char	   *inputFileSpec = NULL;
	extern int	optind;
	extern char *optarg;
	static int	use_setsessauth = 0;
	static int	disable_triggers = 0;
	SegmentDatabase SegDB;
	StatusOp   *pOp;
	struct sigaction act;
	pid_t		newpid;

	/* int i; */
	PQExpBuffer	pszCmdLine;
	int			status;
	int			rc;
	char	   *pszErrorMsg;
	ArchiveHandle *pAH;
	int 		postDataSchemaOnly = 0;

#ifdef USE_DDBOOST
	char    *ddp_file_name = NULL;
	char 	*dd_boost_dir = NULL;
#endif

	struct option cmdopts[] = {
		{"clean", 0, NULL, 'c'},
		{"create", 0, NULL, 'C'},
		{"data-only", 0, NULL, 'a'},
		{"dbname", 1, NULL, 'd'},
		{"exit-on-error", 0, NULL, 'e'},
		{"file", 1, NULL, 'f'},
		{"format", 1, NULL, 'F'},
		{"function", 1, NULL, 'P'},
		{"host", 1, NULL, 'h'},
		{"ignore-version", 0, NULL, 'i'},
		{"index", 1, NULL, 'I'},
		{"list", 0, NULL, 'l'},
		{"no-privileges", 0, NULL, 'x'},
		{"no-acl", 0, NULL, 'x'},
		{"no-owner", 0, NULL, 'O'},
		{"no-reconnect", 0, NULL, 'R'},
		{"port", 1, NULL, 'p'},
		{"password", 0, NULL, 'W'},
		{"schema-only", 0, NULL, 's'},
		{"superuser", 1, NULL, 'S'},
		{"table", 1, NULL, 't'},
		{"trigger", 1, NULL, 'T'},
		{"use-list", 1, NULL, 'L'},
		{"username", 1, NULL, 'U'},
		{"verbose", 0, NULL, 'v'},

		/*
		 * the following options don't have an equivalent short option letter,
		 * but are available as '-X long-name'
		 */
		{"use-set-session-authorization", no_argument, &use_setsessauth, 1},
		{"disable-triggers", no_argument, &disable_triggers, 1},

		/*
		 * the following are cdb specific, and don't have an equivalent short
		 * option
		 */
		{"gp-d", required_argument, NULL, 1},
		{"gp-e", no_argument, NULL, 2},
		{"gp-k", required_argument, NULL, 3},
		{"gp-c", required_argument, NULL, 4},
		{"target-dbid", required_argument, NULL, 5},
		{"target-host", required_argument, NULL, 6},
		{"target-port", required_argument, NULL, 7},
		{"post-data-schema-only", no_argument, &postDataSchemaOnly, 1},
		{"dd_boost_file", required_argument, NULL, 8},
		{"dd_boost_enabled", no_argument, NULL, 9},
		{"dd_boost_dir", required_argument, NULL, 10},
		{"dd_boost_buf_size", required_argument, NULL, 11},
		{"gp-f", required_argument, NULL, 12},
		{"prefix", required_argument, NULL, 13},
		{"status", required_argument, NULL, 14},
		{"netbackup-service-host", required_argument, NULL, 15},
		{"netbackup-block-size", required_argument, NULL, 16},
		{"change-schema-file", required_argument, NULL, 17},
		{"schema-level-file", required_argument, NULL, 18},
		{"ddboost-storage-unit",required_argument, NULL, 19},
		{NULL, 0, NULL, 0}
	};

	set_pglocale_pgservice(argv[0], "pg_dump");

	opts = NewRestoreOptions();

	/* set format default */
	opts->formatName = "p";

	progname = get_progname(argv[0]);

	if (argc > 1)
	{
		if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
		{
			usage(progname);
			exit(0);
		}
		if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
		{
			puts("pg_restore (Greenplum Database) " PG_VERSION);
			exit(0);
		}
	}

	while ((c = getopt_long(argc, argv, "acCd:ef:F:h:iI:lL:Op:P:RsS:t:T:uU:vwWxX:",
							cmdopts, NULL)) != -1)
	{
		switch (c)
		{
			case 'a':			/* Dump data only */
				opts->dataOnly = 1;
				break;
			case 'c':			/* clean (i.e., drop) schema prior to create */
				opts->dropSchema = 1;
				break;
			case 'C':
				opts->createDB = 1;
				break;
			case 'd':
				opts->dbname = strdup(optarg);
				break;
			case 'e':
				opts->exit_on_error = true;
				break;
			case 'f':			/* output file name */
				opts->filename = strdup(optarg);
				break;
			case 'F':
				if (strlen(optarg) != 0)
					opts->formatName = strdup(optarg);
				break;
			case 'h':
				if (strlen(optarg) != 0)
					opts->pghost = strdup(optarg);
				break;
			case 'i':
				/* obsolete option */
				break;

			case 'l':			/* Dump the TOC summary */
				opts->tocSummary = 1;
				break;

			case 'L':			/* input TOC summary file name */
				opts->tocFile = strdup(optarg);
				break;

			case 'O':
				opts->noOwner = 1;
				break;
			case 'p':
				if (strlen(optarg) != 0)
					opts->pgport = strdup(optarg);
				break;
			case 'R':
				/* no-op, still accepted for backwards compatibility */
				break;
			case 'P':			/* Function */
				opts->selTypes = 1;
				opts->selFunction = 1;
				opts->functionNames = strdup(optarg);
				break;
			case 'I':			/* Index */
				opts->selTypes = 1;
				opts->selIndex = 1;
				opts->indexNames = strdup(optarg);
				break;
			case 'T':			/* Trigger */
				opts->selTypes = 1;
				opts->selTrigger = 1;
				opts->triggerNames = strdup(optarg);
				break;
			case 's':			/* dump schema only */
				opts->schemaOnly = 1;
				break;
			case 'S':			/* Superuser username */
				if (strlen(optarg) != 0)
					opts->superuser = strdup(optarg);
				break;
			case 't':			/* Dump data for this table only */
				opts->selTypes = 1;
				opts->selTable = 1;
				opts->tableNames = strdup(optarg);
				break;

			case 'u':
				opts->promptPassword = TRI_YES;
				opts->username = simple_prompt("User name: ", 100, true);
				break;

			case 'U':
				opts->username = optarg;
				break;

			case 'v':			/* verbose */
				opts->verbose = 1;
				break;

			case 'w':
				opts->promptPassword = TRI_NO;
				break;

			case 'W':
				opts->promptPassword = TRI_YES;
				break;

			case 'x':			/* skip ACL dump */
				opts->aclsSkip = 1;
				break;

			case 'X':
				/* -X is a deprecated alternative to long options */
				if (strcmp(optarg, "use-set-session-authorization") == 0)
					use_setsessauth = 1;
				else if (strcmp(optarg, "disable-triggers") == 0)
					disable_triggers = 1;
				else
				{
					fprintf(stderr,
							_("%s: invalid -X option -- %s\n"),
							progname, optarg);
					fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
					exit(1);
				}
				break;

			case 0:
				/* This covers the long options equivalent to -X xxx. */
				break;

			case 1:				/* --gp-d MPP Output Directory */
				g_pszMPPOutputDirectory = strdup(optarg);
				break;

			case 2:				/* --gp-e On Error Stop for psql */
				g_bOnErrorStop = opts->exit_on_error = true;
				break;

			case 3:				/* --gp-k MPP Dump Info Format is
								 * Key_s-dbid_s-role_t-dbid */
				g_gpdumpInfo = strdup(optarg);
				if (!ParseCDBDumpInfo(progname, g_gpdumpInfo, &g_gpdumpKey, &g_role, &g_sourceDBID, &g_MPPPassThroughCredentials))
				{
					exit(1);
				}
				break;
			case 4:				/* gp-c */
				g_compPg = strdup(optarg);
				break;
			case 5:				/* target-dbid */
				g_targetDBID = atoi(strdup(optarg));
				break;
			case 6:				/* target-host */
				g_targetHost = strdup(optarg);
				break;
			case 7:				/* target-port */
				g_targetPort = strdup(optarg);
				break;
#ifdef USE_DDBOOST
			case 9:
				dd_boost_enabled = 1;
				break;
			case 10:
				dd_boost_dir = strdup(optarg);
				break;
			case 11:
				dd_boost_buf_size = strdup(optarg);
				break;
#endif
			case 12:
				table_filter_file = strdup(optarg);
				break;

			case 13:
				dump_prefix = strdup(optarg);
				break;
            /* Hack to pass in the status_file name to cdbbackup.c (gp_restore_launch) */
			case 14:
				break;
			case 15:
				netbackup_service_host = strdup(optarg);
				break;
			case 16:
				netbackup_block_size = strdup(optarg);
				break;
			case 17:
				change_schema_file = strdup(optarg);
				break;
			case 18:
				schema_level_file = strdup(optarg);
				break;
#ifdef USE_DDBOOST
			case 19:
				ddboost_storage_unit = strdup(optarg);
				break;
#endif
			default:
				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
				exit(1);
		}
	}

	/* Should get at most one of -d and -f, else user is confused */
	if (opts->dbname)
	{
		if (opts->filename)
		{
			fprintf(stderr, _("%s: cannot specify both -d and -f output\n"),
					progname);
			fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
					progname);
			exit(1);
		}
		opts->useDB = 1;
	}

	opts->disable_triggers = disable_triggers;
	opts->use_setsessauth = use_setsessauth;

	if (opts->formatName)
	{

		switch (opts->formatName[0])
		{

			case 'c':
			case 'C':
				opts->format = archCustom;
				break;

			case 'f':
			case 'F':
				opts->format = archFiles;
				break;

			case 't':
			case 'T':
				opts->format = archTar;
				break;

			case 'p':
			case 'P':
				bUsePSQL = true;
				break;

			default:
				mpp_err_msg(logInfo, progname, "unrecognized archive format '%s'; please specify 't' or 'c'\n",
							opts->formatName);
				exit(1);
		}
	}

	if (g_gpdumpInfo == NULL)
	{
		mpp_err_msg(logInfo, progname, "missing required parameter gp-k (backup key)\n");
		exit(1);
	}

#ifdef USE_DDBOOST
	if (dd_boost_enabled)
	{
		/* remote is always false when restoring from primary DDR */
		int err = DD_ERR_NONE;
		err = ddp_init("gp_dump");
		if (err != DD_ERR_NONE)
		{
			mpp_err_msg("ERROR", "ddboost", "ddboost init failed. Err = %d\n", err);
			exit(1);
		}

		if (initDDSystem(&ddp_inst, &ddp_conn, &dd_client_info, &ddboost_storage_unit, false, &DEFAULT_BACKUP_DIRECTORY, false))
               	{
			mpp_err_msg(logInfo, progname, "Initializing DD system failed\n");
			exit(1);
		}

		mpp_err_msg(logInfo, progname, "ddboost is initialized\n");

		ddp_file_name = formDDBoostFileName(g_gpdumpKey, postDataSchemaOnly, dd_boost_dir);
		if (ddp_file_name == NULL)
		{
			mpp_err_msg(logInfo, progname, "Error in opening ddboost file\n");
			exit(1);
		}
	}
#endif

	SegDB.dbid = g_sourceDBID;
	SegDB.role = g_role;
	SegDB.port = opts->pgport ? atoi(opts->pgport) : 5432;
	SegDB.pszHost = opts->pghost ? strdup(opts->pghost) : NULL;
	SegDB.pszDBName = opts->dbname ? strdup(opts->dbname) : NULL;
	SegDB.pszDBUser = opts->username ? strdup(opts->username) : NULL;
	SegDB.pszDBPswd = NULL;

	if (g_MPPPassThroughCredentials != NULL && *g_MPPPassThroughCredentials != '\0')
	{
		unsigned int nBytes;
		char	   *pszDBPswd = Base64ToData(g_MPPPassThroughCredentials, &nBytes);

		if (pszDBPswd == NULL)
		{
			mpp_err_msg(logError, progname, "Invalid Greenplum DB Credentials:  %s\n", g_MPPPassThroughCredentials);
			exit(1);
		}
		if (nBytes > 0)
		{
			SegDB.pszDBPswd = malloc(nBytes + 1);
			if (SegDB.pszDBPswd == NULL)
			{
				mpp_err_msg(logInfo, progname, "Cannot allocate memory for Greenplum Database Credentials\n");
				exit(1);
			}

			memcpy(SegDB.pszDBPswd, pszDBPswd, nBytes);
			SegDB.pszDBPswd[nBytes] = '\0';
		}
	}

	if (g_role == ROLE_MASTER)
		g_conn = MakeDBConnection(&SegDB, true);
	else
		g_conn = MakeDBConnection(&SegDB, false);
	if (PQstatus(g_conn) == CONNECTION_BAD)
	{
		exit_horribly(NULL, NULL, "connection to database \"%s\" failed: %s",
					  PQdb(g_conn), PQerrorMessage(g_conn));
	}

	if (g_gpdumpKey != NULL)
	{
		/*
		 * Open the database again, for writing status info
		 */
		g_conn_status = MakeDBConnection(&SegDB, false);

		if (PQstatus(g_conn_status) == CONNECTION_BAD)
		{
			exit_horribly(NULL, NULL, "Connection on host %s failed: %s",
						  StringNotNull(SegDB.pszHost, "localhost"),
						  PQerrorMessage(g_conn_status));
		}

		g_main_tid = pthread_self();

		g_pStatusOpList = CreateStatusOpList();
		if (g_pStatusOpList == NULL)
		{
			exit_horribly(NULL, NULL, "cannot allocate memory for gp_backup_status operation\n");
		}

		/*
		 * Create thread for monitoring for cancel requests.  If we're running
		 * using PSQL, the monitor is not allowed to start until the worker
		 * process is forked.  This is done to prevent the forked process from
		 * being blocked by locks held by library routines (__tz_convert, for
		 * example).
		 */
		if (bUsePSQL)
		{
			pthread_mutex_lock(&g_threadSyncPoint);
		}
		pthread_create(&g_monitor_tid,
					   NULL,
					   monitorThreadProc,
					   NULL);

		/* Install Ctrl-C interrupt handler, now that we have a connection */
		if (!bUsePSQL)
		{
			act.sa_handler = myHandler;
			sigemptyset(&act.sa_mask);
			act.sa_flags = 0;
			act.sa_flags |= SA_RESTART;
			if (sigaction(SIGINT, &act, NULL) < 0)
			{
				mpp_err_msg(logInfo, progname, "Error trying to set SIGINT interrupt handler\n");
			}

			act.sa_handler = myHandler;
			sigemptyset(&act.sa_mask);
			act.sa_flags = 0;
			act.sa_flags |= SA_RESTART;
			if (sigaction(SIGTERM, &act, NULL) < 0)
			{
				mpp_err_msg(logInfo, progname, "Error trying to set SIGTERM interrupt handler\n");
			}
		}

		pOp = CreateStatusOp(TASK_START, TASK_RC_SUCCESS, SUFFIX_START, TASK_MSG_SUCCESS);
		if (pOp == NULL)
		{
			exit_horribly(NULL, NULL, "cannot allocate memory for gp_backup_status operation\n");
		}
		AddToStatusOpList(g_pStatusOpList, pOp);
	}
	/* end cdb additions */

	if (bUsePSQL)
	{
		/* Install Ctrl-C interrupt handler, now that we have a connection */
		act.sa_handler = psqlHandler;
		sigemptyset(&act.sa_mask);
		act.sa_flags = 0;
		act.sa_flags |= SA_RESTART;
		if (sigaction(SIGINT, &act, NULL) < 0)
		{
			mpp_err_msg(logInfo, progname, "Error trying to set SIGINT interrupt handler\n");
		}

		act.sa_handler = psqlHandler;
		sigemptyset(&act.sa_mask);
		act.sa_flags = 0;
		act.sa_flags |= SA_RESTART;
		if (sigaction(SIGTERM, &act, NULL) < 0)
		{
			mpp_err_msg(logInfo, progname, "Error trying to set SIGTERM interrupt handler\n");
		}

		/* Establish a SIGCHLD handler to catch termination the psql process */
		act.sa_handler = myChildHandler;
		sigemptyset(&act.sa_mask);
		act.sa_flags = 0;
		act.sa_flags |= SA_RESTART;
		if (sigaction(SIGCHLD, &act, NULL) < 0)
		{
			mpp_err_msg(logInfo, progname, "Error trying to set SIGCHLD interrupt handler\n");
			exit(1);
		}

		mpp_err_msg(logInfo, progname, "Before fork of gp_restore_agent\n");

		newpid = fork();
		if (newpid < 0)
		{
			mpp_err_msg(logError, progname, "Failed to fork\n");
		}
		else if (newpid == 0)
		{
			/* TODO: use findAcceptableBackupFilePathName(...) to look for the file name
			 *       if user invoked gp_restore_agent directly without supplying a file name.
			 *       If the agent is invoked from gp_restore_launch, then we are ok.
			 */
			if (optind < argc)
			{
				char *rawInputFile = argv[optind];

				valueBuf = createPQExpBuffer();
				inputFileSpec = shellEscape(rawInputFile, valueBuf, false, false);
			}

			if (inputFileSpec == NULL || inputFileSpec[0] == '\0')
			{
				mpp_err_msg(logError, progname, "dump file path is empty");
				exit(1);
			}

			if (postDataSchemaOnly)
			{
				if (strstr(inputFileSpec,"_post_data") == NULL)
				{
					fprintf(stderr,"Adding _post_data to the end of the file name?\n");
					char * newFS = malloc(strlen(inputFileSpec) + strlen("_post_data") + 1);
					strcpy(newFS, inputFileSpec);
					strcat(newFS, "_post_data");
					inputFileSpec = newFS;
				}
			}

			pszCmdLine = createPQExpBuffer();
#ifdef USE_DDBOOST
			if (dd_boost_enabled)
			{
				formDDBoostPsqlCommandLine(pszCmdLine, argv[0],
									(postDataSchemaOnly == 1 ? true : false),
									g_role, g_compPg, ddp_file_name,
									dd_boost_buf_size, table_filter_file,
									change_schema_file, schema_level_file,
									ddboost_storage_unit);
			}
			else
			{
#endif
				formPsqlCommandLine(pszCmdLine, argv[0], (postDataSchemaOnly == 1), g_role,
									inputFileSpec, g_compPg, table_filter_file,
									netbackup_service_host, netbackup_block_size,
									change_schema_file, schema_level_file);
#ifdef USE_DDBOOST
			}
#endif

			appendPQExpBuffer(pszCmdLine, " -h %s -p %s -U %s -d ",
							  g_targetHost, g_targetPort, SegDB.pszDBUser);
			shellEscape(SegDB.pszDBName, pszCmdLine, true /* quote */, false /* reset */);
			appendPQExpBuffer(pszCmdLine, " -a ");

			if (g_bOnErrorStop)
				appendPQExpBuffer(pszCmdLine, " -v ON_ERROR_STOP=");

			if (g_role == ROLE_SEGDB)
				putenv("PGOPTIONS=-c gp_session_role=UTILITY");
			if (g_role == ROLE_MASTER)
				putenv("PGOPTIONS=-c gp_session_role=DISPATCH");

			mpp_err_msg(logInfo, progname, "Command Line: %s\n", pszCmdLine->data);

			/*
			 * Make this new process the process group leader of the children
			 * being launched.	This allows a signal to be sent to all
			 * processes in the group simultaneously.
			 */
			setpgid(newpid, newpid);

			execl("/bin/sh", "sh", "-c", pszCmdLine->data, NULL);

			mpp_err_msg(logInfo, progname, "Error in gp_restore_agent - execl of %s with Command Line %s failed",
						"/bin/sh", pszCmdLine->data);

			_exit(127);
		}
		else
		{
			/*
			 * Make the new child process the process group leader of the
			 * children being launched.  This allows a signal to be sent to
			 * all processes in the group simultaneously.
			 *
			 * This is a redundant call to avoid a race condition suggested by
			 * Stevens.
			 */
			setpgid(newpid, newpid);

			/* Allow the monitor thread to begin execution. */
			pthread_mutex_unlock(&g_threadSyncPoint);

			/* Parent .  Lets sleep and wake up until we see it's done */
			while (!bPSQLDone)
			{
				sleep(5);
			}

			/*
			 * If this process has been sent a SIGINT or SIGTERM, we need to
			 * send a SIGINT to the psql process GROUP.
			 */
			if (bKillPsql)
			{
				mpp_err_msg(logInfo, progname, "Terminating psql due to signal.\n");
				kill(-newpid, SIGINT);
			}

			waitpid(newpid, &status, 0);
			if (WIFEXITED(status))
			{
				rc = WEXITSTATUS(status);
				if (rc == 0)
				{
					mpp_err_msg(logInfo, progname, "psql finished with rc %d.\n", rc);
					/* Normal completion falls to end of routine. */
				}
				else
				{
					if (rc >= 128)
					{
						/*
						 * If the exit code has the 128-bit set, the exit code
						 * represents a shell exited by signal where the
						 * signal number is exitCode - 128.
						 */
						rc -= 128;
						pszErrorMsg = MakeString("psql finished abnormally with signal number %d.\n", rc);
					}
					else
					{
						pszErrorMsg = MakeString("psql finished abnormally with return code %d.\n", rc);
					}
					makeSureMonitorThreadEnds(TASK_RC_FAILURE, pszErrorMsg);
					free(pszErrorMsg);
					exit_code = 2;
				}
			}
			else if (WIFSIGNALED(status))
			{
				pszErrorMsg = MakeString("psql finished abnormally with signal number %d.\n", WTERMSIG(status));
				mpp_err_msg(logError, progname, pszErrorMsg);
				makeSureMonitorThreadEnds(TASK_RC_FAILURE, pszErrorMsg);
				free(pszErrorMsg);
				exit_code = 2;
			}
			else
			{
				pszErrorMsg = MakeString("psql crashed or finished badly; status=%#x.\n", status);
				mpp_err_msg(logError, progname, pszErrorMsg);
				makeSureMonitorThreadEnds(TASK_RC_FAILURE, pszErrorMsg);
				free(pszErrorMsg);
				exit_code = 2;
			}
		}
	}
	else
	{
		AH = OpenArchive(inputFileSpec, opts->format);

		/* Let the archiver know how noisy to be */
		AH->verbose = opts->verbose;

		/*
	     * Whether to keep submitting sql commands as "pg_restore ... | psql ... "
		 */
		AH->exit_on_error = opts->exit_on_error;

		if (opts->tocFile)
			SortTocFromFile(AH, opts);

		if (opts->tocSummary)
			PrintTOCSummary(AH, opts);
		else
		{
			pAH = (ArchiveHandle *) AH;

			if (opts->useDB)
			{
				/* check for version mismatch */
				if (pAH->version < K_VERS_1_3)
					die_horribly(NULL, NULL, "direct database connections are not supported in pre-1.3 archives\n");

				pAH->connection = g_conn;
				/* XXX Should get this from the archive */
				AH->minRemoteVersion = 070100;
				AH->maxRemoteVersion = 999999;

				_check_database_version(pAH);
			}

			RestoreArchive(AH, opts);

			/*
			 * The following is necessary when the -C option is used.  A new
			 * connection is gotten to the database within RestoreArchive
			 */
			if (pAH->connection != g_conn)
				g_conn = pAH->connection;
		}

		/* done, print a summary of ignored errors */
		if (AH->n_errors)
			fprintf(stderr, _("WARNING: errors ignored on restore: %d\n"),
					AH->n_errors);

		/* AH may be freed in CloseArchive? */
		exit_code = AH->n_errors ? 1 : 0;

		CloseArchive(AH);
	}

#ifdef USE_DDBOOST
	if(dd_boost_enabled)
		cleanupDDSystem();
	free(ddboost_storage_unit);
#endif

	makeSureMonitorThreadEnds(TASK_RC_SUCCESS, TASK_MSG_SUCCESS);

	DestroyStatusOpList(g_pStatusOpList);

	if (change_schema_file)
		free(change_schema_file);
	if (schema_level_file)
		free(schema_level_file);
	if (SegDB.pszHost)
		free(SegDB.pszHost);
	if (SegDB.pszDBName)
		free(SegDB.pszDBName);
	if (SegDB.pszDBUser)
		free(SegDB.pszDBUser);
	if (SegDB.pszDBPswd)
		free(SegDB.pszDBPswd);
	if (valueBuf)
		destroyPQExpBuffer(valueBuf);
	if (escapeBuf)
		destroyPQExpBuffer(escapeBuf);

	PQfinish(g_conn);
	if (exit_code == 0)
		mpp_err_msg(logInfo, progname, "Finished successfully\n");
	else
		mpp_err_msg(logError, progname, "Finished with errors\n");
	return exit_code;
}
Esempio n. 26
0
/*
 * Make a database connection with the given parameters.  The
 * connection handle is returned, the parameters are stored in AHX.
 * An interactive password prompt is automatically issued if required.
 *
 * Note: it's not really all that sensible to use a single-entry password
 * cache if the username keeps changing.  In current usage, however, the
 * username never does change, so one savedPassword is sufficient.
 */
void
ConnectDatabase(Archive *AHX,
				const char *dbname,
				const char *pghost,
				const char *pgport,
				const char *username,
				trivalue prompt_password)
{
	ArchiveHandle *AH = (ArchiveHandle *) AHX;
	char	   *password;
	bool		new_pass;

	if (AH->connection)
		exit_horribly(modulename, "already connected to a database\n");

	password = AH->savedPassword ? pg_strdup(AH->savedPassword) : NULL;

	if (prompt_password == TRI_YES && password == NULL)
	{
		password = simple_prompt("Password: "******"out of memory\n");
	}
	AH->promptPassword = prompt_password;

	/*
	 * Start the connection.  Loop until we have a password if requested by
	 * backend.
	 */
	do
	{
		const char *keywords[7];
		const char *values[7];

		keywords[0] = "host";
		values[0] = pghost;
		keywords[1] = "port";
		values[1] = pgport;
		keywords[2] = "user";
		values[2] = username;
		keywords[3] = "password";
		values[3] = password;
		keywords[4] = "dbname";
		values[4] = dbname;
		keywords[5] = "fallback_application_name";
		values[5] = progname;
		keywords[6] = NULL;
		values[6] = NULL;

		new_pass = false;
		AH->connection = PQconnectdbParams(keywords, values, true);

		if (!AH->connection)
			exit_horribly(modulename, "failed to connect to database\n");

		if (PQstatus(AH->connection) == CONNECTION_BAD &&
			PQconnectionNeedsPassword(AH->connection) &&
			password == NULL &&
			prompt_password != TRI_NO)
		{
			PQfinish(AH->connection);
			password = simple_prompt("Password: "******"out of memory\n");
			new_pass = true;
		}
	} while (new_pass);

	/* check to see that the backend connection was successfully made */
	if (PQstatus(AH->connection) == CONNECTION_BAD)
		exit_horribly(modulename, "connection to database \"%s\" failed: %s",
					  PQdb(AH->connection) ? PQdb(AH->connection) : "",
					  PQerrorMessage(AH->connection));

	/*
	 * We want to remember connection's actual password, whether or not we got
	 * it by prompting.  So we don't just store the password variable.
	 */
	if (PQconnectionUsedPassword(AH->connection))
	{
		if (AH->savedPassword)
			free(AH->savedPassword);
		AH->savedPassword = pg_strdup(PQpass(AH->connection));
	}
	if (password)
		free(password);

	/* check for version mismatch */
	_check_database_version(AH);

	PQsetNoticeProcessor(AH->connection, notice_processor, NULL);
}
Esempio n. 27
0
/*
 * Connect to the db again.
 *
 * Note: it's not really all that sensible to use a single-entry password
 * cache if the username keeps changing.  In current usage, however, the
 * username never does change, so one savedPassword is sufficient.  We do
 * update the cache on the off chance that the password has changed since the
 * start of the run.
 */
static PGconn *
_connectDB(ArchiveHandle *AH, const char *reqdb, const char *requser)
{
	PGconn	   *newConn;
	const char *newdb;
	const char *newuser;
	char	   *password;
	bool		new_pass;

	if (!reqdb)
		newdb = PQdb(AH->connection);
	else
		newdb = reqdb;

	if (!requser || strlen(requser) == 0)
		newuser = PQuser(AH->connection);
	else
		newuser = requser;

	ahlog(AH, 1, "connecting to database \"%s\" as user \"%s\"\n",
		  newdb, newuser);

	password = AH->savedPassword ? pg_strdup(AH->savedPassword) : NULL;

	if (AH->promptPassword == TRI_YES && password == NULL)
	{
		password = simple_prompt("Password: "******"out of memory\n");
	}

	do
	{
		const char *keywords[7];
		const char *values[7];

		keywords[0] = "host";
		values[0] = PQhost(AH->connection);
		keywords[1] = "port";
		values[1] = PQport(AH->connection);
		keywords[2] = "user";
		values[2] = newuser;
		keywords[3] = "password";
		values[3] = password;
		keywords[4] = "dbname";
		values[4] = newdb;
		keywords[5] = "fallback_application_name";
		values[5] = progname;
		keywords[6] = NULL;
		values[6] = NULL;

		new_pass = false;
		newConn = PQconnectdbParams(keywords, values, true);

		if (!newConn)
			exit_horribly(modulename, "failed to reconnect to database\n");

		if (PQstatus(newConn) == CONNECTION_BAD)
		{
			if (!PQconnectionNeedsPassword(newConn))
				exit_horribly(modulename, "could not reconnect to database: %s",
							  PQerrorMessage(newConn));
			PQfinish(newConn);

			if (password)
				fprintf(stderr, "Password incorrect\n");

			fprintf(stderr, "Connecting to %s as %s\n",
					newdb, newuser);

			if (password)
				free(password);

			if (AH->promptPassword != TRI_NO)
				password = simple_prompt("Password: "******"connection needs password\n");

			if (password == NULL)
				exit_horribly(modulename, "out of memory\n");
			new_pass = true;
		}
	} while (new_pass);

	/*
	 * We want to remember connection's actual password, whether or not we got
	 * it by prompting.  So we don't just store the password variable.
	 */
	if (PQconnectionUsedPassword(newConn))
	{
		if (AH->savedPassword)
			free(AH->savedPassword);
		AH->savedPassword = pg_strdup(PQpass(newConn));
	}
	if (password)
		free(password);

	/* check for version mismatch */
	_check_database_version(AH);

	PQsetNoticeProcessor(newConn, notice_processor, NULL);

	return newConn;
}
Esempio n. 28
0
static void
ReadDataFromArchiveZlib(ArchiveHandle *AH, ReadFunc readF)
{
	z_streamp	zp;
	char	   *out;
	int			res = Z_OK;
	size_t		cnt;
	char	   *buf;
	size_t		buflen;

	zp = (z_streamp) pg_malloc(sizeof(z_stream));
	zp->zalloc = Z_NULL;
	zp->zfree = Z_NULL;
	zp->opaque = Z_NULL;

	buf = pg_malloc(ZLIB_IN_SIZE);
	buflen = ZLIB_IN_SIZE;

	out = pg_malloc(ZLIB_OUT_SIZE + 1);

	if (inflateInit(zp) != Z_OK)
		exit_horribly(modulename,
					  "could not initialize compression library: %s\n",
					  zp->msg);

	/* no minimal chunk size for zlib */
	while ((cnt = readF(AH, &buf, &buflen)))
	{
		zp->next_in = (void *) buf;
		zp->avail_in = cnt;

		while (zp->avail_in > 0)
		{
			zp->next_out = (void *) out;
			zp->avail_out = ZLIB_OUT_SIZE;

			res = inflate(zp, 0);
			if (res != Z_OK && res != Z_STREAM_END)
				exit_horribly(modulename,
							  "could not uncompress data: %s\n", zp->msg);

			out[ZLIB_OUT_SIZE - zp->avail_out] = '\0';
			ahwrite(out, 1, ZLIB_OUT_SIZE - zp->avail_out, AH);
		}
	}

	zp->next_in = NULL;
	zp->avail_in = 0;
	while (res != Z_STREAM_END)
	{
		zp->next_out = (void *) out;
		zp->avail_out = ZLIB_OUT_SIZE;
		res = inflate(zp, 0);
		if (res != Z_OK && res != Z_STREAM_END)
			exit_horribly(modulename,
						  "could not uncompress data: %s\n", zp->msg);

		out[ZLIB_OUT_SIZE - zp->avail_out] = '\0';
		ahwrite(out, 1, ZLIB_OUT_SIZE - zp->avail_out, AH);
	}

	if (inflateEnd(zp) != Z_OK)
		exit_horribly(modulename,
					  "could not close compression library: %s\n", zp->msg);

	free(buf);
	free(out);
	free(zp);
}