OpenSolaris_b135/lib/mms/mgmt/common/mgmt_mmsdb.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */


#include <strings.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <ctype.h>
#include <libnvpair.h>

#include "mms_mgmt.h"
#include "mgmt_util.h"
#include "mms_cfg.h"
#include "net_cfg_service.h"

static char *_SrcFile = __FILE__;
#define	HERE _SrcFile, __LINE__
#define	MMS_MGMT_PGA	".pga"

typedef struct {
	char	port[10];
	char	user[256];
	char	bindir[MAXPATHLEN];
	char	path[MAXPATHLEN];
	char	logdir[MAXPATHLEN];
	char	dbname[MAXPATHLEN];
	char	dbhost[MAXHOSTNAMELEN];
	uid_t	dbuid;
	gid_t	dbgid;
} mmsdb_opts_t;

/* If this path changes, make sure similar changes are made to mmsexplorer */
static char db_cli_env[1024];

static int get_db_user(char *buf, int buflen, uid_t *uid, gid_t *gid);
static int configure_pgconf(char *port, char *logdir);
static int get_dbver_from_optfile(char *path, int *version);
static int mk_cmds_from_optfile(mmsdb_opts_t *opts, char *path, int vers,
	char cmdtype, int dopd, char **cmdfile);
static int mgmt_db_sql_exec(char *cmdfile, mmsdb_opts_t *opts);
static int set_mm_system_vars_db(nvlist_t *opts, char *cmdfile);
static int mgmt_get_db_opts(mmsdb_opts_t *opts);
static int create_db_dirs(char *dbpath, uid_t uid, gid_t gid, nvlist_t *errs);
static int update_pghba(boolean_t ismd5, mmsdb_opts_t *dbopts, nvlist_t *errs);


/*
 *  Functions to manage the MMS Database
 */

int
mgmt_set_db_opts(nvlist_t *opts, nvlist_t *errlist)
{
	int			st = 0;
	mmsdb_opts_t		oldopts;
	int			doconf = 0;
	uid_t			uid = 0;
	gid_t			gid = 0;
	char			*val;
	char			*port = NULL;
	char			*logdir = NULL;

	if (!mgmt_chk_auth("solaris.mms.modify")) {
		return (EACCES);
	}

	/*
	 * check to see if any options have been set for the DB yet.  Only
	 * a few are user-configurable.   Changing the DB user is handled
	 * by the admin with SMF and chown -R. (i.e., outside of our CLI,
	 * at least for now)
	 */
	(void) memset(&oldopts, 0, sizeof (mmsdb_opts_t));

	st = mgmt_get_db_opts(&oldopts);
	if (st != 0) {
		return (st);
	}

	uid = oldopts.dbuid;
	gid = oldopts.dbgid;

	/* TODO - should we allow change after set?  Need to copy -R if so */
	st = nvlist_lookup_string(opts, O_DBDIR, &val);
	if (st == 0) {
		st = create_db_dirs(val, uid, gid, errlist);
		if (st != 0) {
			return (st);
		}
	}

	st = nvlist_lookup_string(opts, O_DBPORT, &port);
	if (st == 0) {
		/* update the conf file */
		doconf = 1;
	}

	st = nvlist_lookup_string(opts, O_DBLOG, &logdir);
	if (st == 0) {
		/* update the conf file */
		doconf = 1;
		/* create dblogdir if it doesn't exist */
		st = create_dir(logdir, 0711, NULL, uid, NULL, gid);
	}

	if ((st == 0) && (doconf)) {
		st = configure_pgconf(port, logdir);
	}

	return (st);
}

static int
create_db_dirs(char *dbpath, uid_t uid, gid_t gid, nvlist_t *errs)
{
	int		st;
	struct stat64	statbuf;
	char		*dbsubdirs[] = {"dump", "log", NULL};
	int		i;
	char		buf[2048];

	if (!dbpath) {
		return (MMS_MGMT_NOARG);
	}

	st = stat64(dbpath, &statbuf);
	if ((st != 0) && (errno != ENOENT)) {
		st = errno;
		MGMT_ADD_ERR(errs, dbpath, st);
		return (st);
	}

	st = create_dir(dbpath, 0711, NULL, uid, NULL, gid);
	if (st != 0) {
		st = errno;
		MGMT_ADD_ERR(errs, dbpath, st);
		return (st);
	}

	for (i = 0; dbsubdirs[i] != NULL; i++) {
		(void) snprintf(buf, sizeof (buf), "%s/../%s", dbpath,
		    dbsubdirs[i]);
		st = create_dir(buf, 0711, NULL, uid, NULL, gid);
		if (st != 0) {
			st = errno;
			MGMT_ADD_ERR(errs, buf, st);
			break;
		}
	}

	return (st);
}

int
mgmt_db_init(void)
{
	int		st;
	pid_t		pid;
	char		buf[2048];
	char		dbbuf[2048];
	mmsdb_opts_t	opts;
	char		*cmd[4];

	if (!mgmt_chk_auth("solaris.mms.create")) {
		return (EACCES);
	}

	st = mgmt_get_db_opts(&opts);
	if (st != 0) {
		mms_trace(MMS_DEBUG,
		    "mgmt_get_db_opts(&opts) error");
		return (st);
	}

	/* see if we've been initialized already, bail if so */
	(void) snprintf(buf, sizeof (buf), "%s/postgresql.conf", opts.path);
	st = access(buf, F_OK);
	if (st == 0) {
		return (0);
	}

	(void) snprintf(dbbuf, sizeof (dbbuf), "%s/initdb", opts.bindir);

	cmd[0] = dbbuf;
	cmd[1] = "-D";
	cmd[2] = opts.path;
	cmd[3] = NULL;

	pid = exec_mgmt_cmd(NULL, NULL, opts.dbuid, opts.dbgid,
	    B_FALSE, cmd);
	mms_trace(MMS_DEBUG, "exec_mgmt_cmd: %s %s %s", cmd[0], cmd[1], cmd[2]);

	st = check_exit(pid, NULL);
	if (st != 0)
		mms_trace(MMS_DEBUG,
		    "exec_mgmt_cmd error");

	return (st);
}

static int
mgmt_get_db_opts(mmsdb_opts_t *opts)
{
	int		st;
	struct passwd	pwd;
	struct passwd	*pwdp;
	char		buf[2048];

	if (opts == NULL) {
		return (MMS_MGMT_NOARG);
	}

	st = mms_cfg_getvar(MMS_CFG_DB_DATA, opts->path);
	if (st == 0) {
		st = mms_cfg_getvar(MMS_CFG_MM_DB_USER, opts->user);
	}
	if (st == 0) {
		st = mms_cfg_getvar(MMS_CFG_MM_DB_HOST, opts->dbhost);
	}
	if (st == 0) {
		st = mms_cfg_getvar(MMS_CFG_MM_DB_PORT, opts->port);
	}
	if (st == 0) {
		st = mms_cfg_getvar(MMS_CFG_MM_DB_NAME, opts->dbname);
	}
	if (st == 0) {
		st = mms_cfg_getvar(MMS_CFG_DB_BIN, opts->bindir);
	}
	if (st == 0) {
		(void) snprintf(opts->logdir, sizeof (opts->logdir),
		    "%s/../log", opts->path);
	}

	if (st != 0) {
		return (st);
	}

	(void) getpwnam_r(opts->user, &pwd, buf, sizeof (buf), &pwdp);
	if (pwdp == NULL) {
		return (MMS_MGMT_DB_USER_NOTFOUND);
	}

	opts->dbuid = pwdp->pw_uid;
	opts->dbgid = pwdp->pw_gid;

	/* set the envvar for PGPASSFILE */
	(void) snprintf(db_cli_env, sizeof (db_cli_env),
	    "PGPASSFILE=%s/../%s", opts->path, MMS_MGMT_PGA);
	st = putenv(db_cli_env);

	return (st);
}

static int
get_db_user(char *buf, int buflen, uid_t *uid, gid_t *gid)
{
	int		st;
	struct passwd	pwd;
	struct passwd	*pwdp;

	if ((buf == NULL) || (uid == NULL)) {
		return (MMS_MGMT_NOARG);
	}

	st = mms_cfg_getvar(MMS_CFG_MM_DB_USER, buf);
	if (st != 0) {
		return (st);
	}

	(void) getpwnam_r(buf, &pwd, buf, buflen, &pwdp);
	if (pwdp == NULL) {
		return (MMS_MGMT_DB_USER_NOTFOUND);
	}

	*uid = pwdp->pw_uid;
	if (gid != NULL) {
		*gid = pwdp->pw_gid;
	}

	return (0);
}

int
mgmt_db_create(int initialize, int populate, nvlist_t *optlist)
{
	int		st;
	pid_t		pid;
	mmsdb_opts_t	opts;
	int		oldver = -1;
	int		ver = -1;
	char		buf[MAXPATHLEN];
	char		*pkgfile = MMSETCDIR"/db/mms_db";
	char		*cmd[7];
	char		dbbuf[2048];

	mms_trace(MMS_DEBUG, "Creating the MMS Database");

	if (!mgmt_chk_auth("solaris.mms.modify")) {
		mms_trace(MMS_DEBUG,
		    "mgmt_chk_auth(\"solaris.mms.modify\") error");
		return (EACCES);
	}

	st = mgmt_get_db_opts(&opts);
	if (st != 0) {
		mms_trace(MMS_DEBUG,
		    "mgmt_get_db_opts(&opts) error");
		return (st);
	}

	(void) snprintf(dbbuf, sizeof (dbbuf), "%s/createdb", opts.bindir);

	cmd[0] = dbbuf;
	cmd[1] = "-h";
	cmd[2] = opts.dbhost;
	cmd[3] = "-p";
	cmd[4] = opts.port;
	cmd[5] = opts.dbname;
	cmd[6] = NULL;

	if (initialize) {
		st = mgmt_db_check();
		if (st == 0) {
			mms_trace(MMS_DEBUG,
			    "EALREADY error");
			/* db is alive, already inited */
			return (EALREADY);
		}

		/* check to see if files exist, even if svc is stopped */
		(void) snprintf(buf, sizeof (buf), "%s/base", opts.path);
		if (access(buf, F_OK) == 0) {
			mms_trace(MMS_DEBUG,
			    "access(buf, F_OK) error");
			return (EALREADY);
		}

		st = mgmt_db_init();
		if (st != 0)
			mms_trace(MMS_DEBUG,
			    "mgmt_db_init() error");
		if (st == 0) {
			st = configure_pgconf(opts.port, opts.logdir);
		}

		if (st != 0) {
			mms_trace(MMS_DEBUG,
			    "configure_pgconf error");
			return (st);
		}

		/* create the dirs we need */
		st = create_db_dirs(opts.path, opts.dbuid, opts.dbgid, NULL);
		if (st != 0) {
			mms_trace(MMS_DEBUG,
			    "create_db_dirs error");
			return (st);
		}
	}

	/*
	 * some callers may wish to populate the DB themselves, as in
	 * upgrade or downgrade.
	 */
	if (populate) {
		st = get_dbver_from_optfile(pkgfile, &ver);
		if (st != 0) {
			mms_trace(MMS_DEBUG,
			    "get_dbver_from_optfile error");
			return (st);
		}

		(void) snprintf(buf, sizeof (buf), "%s/../mmsdb", opts.path);
		st = get_dbver_from_optfile(buf, &oldver);
		if (st != 0) {
			if (st != ENOENT) {
				mms_trace(MMS_DEBUG,
				    "get_dbver_from_optfile error");
				return (st);
			}
			st = 0;
		}

		if (ver > oldver) {
			if (oldver != -1) {
				/* save the old mod file */
				char	newf[MAXPATHLEN];

				(void) snprintf(newf, sizeof (newf), "%s-%d",
				    buf, oldver);
				(void) rename(buf, newf);
			}
			st = cp_file(pkgfile, buf);
			if (st != 0) {
				return (st);
			}
			(void) chown(buf, opts.dbuid, opts.dbgid);
		}
	}

	/* make sure the DB is running */
	mms_trace(MMS_DEBUG, "enable db");
	st = mgmt_set_svc_state(DBSVC, ENABLE, NULL);
	if (st != 0) {
		mms_trace(MMS_DEBUG,
		    "mgmt_set_svc_state error");
		return (st);
	}

	st = mgmt_db_ready();
	if (st != 0) {
		mms_trace(MMS_ERR,
		    "database did not go ready");
		return (st);
	}

	pid = exec_mgmt_cmd(NULL, NULL, opts.dbuid, opts.dbgid,
	    B_FALSE, cmd);
	mms_trace(MMS_DEBUG,
	    "exec_mgmt_cmd: %s %s %s %s %s %s",
	    cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5]);

	st = check_exit(pid, NULL);
	if (st != 0) {
		mms_trace(MMS_DEBUG,
		    "exec_mgmt_cmd error: st %d", st);
	}

	if ((st == 0) && (populate)) {
		/* import all the sql cmds */
		char		*cmdfile = NULL;

		st = mk_cmds_from_optfile(&opts, buf, ver, 'u', 1, &cmdfile);
		if ((st == 0) && optlist) {
			st = set_mm_system_vars_db(optlist, cmdfile);
		}
		if (st == 0) {
			st = mgmt_db_sql_exec(cmdfile, &opts);
		}

		if (cmdfile) {
			(void) unlink(cmdfile);
			free(cmdfile);
		}

		if (st == 0) {
			char	*passp;

			st = nvlist_lookup_string(optlist, O_MMPASS, &passp);
			if (st == 0) {
				(void) snprintf(buf, sizeof (buf),
				    "%s_dbadmin", passp);
				st = mgmt_set_db_pass(buf, NULL);
			}
		}
	}

	return (st);
}

int
mgmt_db_drop(void)
{
	int		st;
	pid_t		pid;
	mmsdb_opts_t	opts;
	char		*cmd[5];
	char		dbbuf[2048];

	if (!mgmt_chk_auth("solaris.mms.modify")) {
		return (EACCES);
	}

	st = mgmt_get_db_opts(&opts);
	if (st != 0) {
		return (st);
	}

	(void) snprintf(dbbuf, sizeof (dbbuf), "%s/dropdb", opts.bindir);

	cmd[0] = dbbuf;
	cmd[1] = "-p";
	cmd[2] = opts.port;
	cmd[3] = opts.dbname;
	cmd[4] = NULL;

	pid = exec_mgmt_cmd(NULL, NULL, opts.dbuid, opts.dbgid,
	    B_FALSE, cmd);
	mms_trace(MMS_DEBUG, "exec_mgmt_cmd: %s %s %s %s",
	    cmd[0], cmd[1], cmd[2], cmd[3]);


	st = check_exit(pid, NULL);

	if (st != 0) {
		/* restart the service to force users to disconnect */

		mms_trace(MMS_DEBUG, "restart db");
		(void) mgmt_set_svc_state(DBSVC, RESTART, NULL);

		st = mgmt_db_ready();
		if (st != 0) {
			mms_trace(MMS_ERR,
			    "database did not go ready");
		}

		if (st == 0) {
			pid = exec_mgmt_cmd(NULL, NULL, opts.dbuid, opts.dbgid,
			    B_FALSE, cmd);
			mms_trace(MMS_DEBUG, "exec_mgmt_cmd: %s %s %s %s",
			    cmd[0], cmd[1], cmd[2], cmd[3]);

			st = check_exit(pid, NULL);
		}
	}

	return (st);
}

int
mgmt_db_ready(void)
{
	int		i;
	int		st;
	pid_t		pid;
	FILE		*readf = NULL;
	mmsdb_opts_t	opts;
	char		*cmd[7];
	char		dbbuf[2048];

	if (!mgmt_chk_auth("solaris.mms.modify")) {
		return (EACCES);
	}

	st = mgmt_get_db_opts(&opts);
	if (st != 0) {
		return (st);
	}

	mms_trace(MMS_DEBUG, "check for database ready");

	(void) snprintf(dbbuf, sizeof (dbbuf), "%s/psql", opts.bindir);

	/* simple test to list available databases to check for db ready */
	cmd[0] = dbbuf;
	cmd[1] = "-h";
	cmd[2] = opts.dbhost;
	cmd[3] = "-p";
	cmd[4] = opts.port;
	cmd[5] = "-l";
	cmd[6] = NULL;

	for (i = 0; i < 30; i++) {

		/* when the simple test is successful the */
		/* database is ready for socket connections */

		pid = exec_mgmt_cmd(&readf, NULL, opts.dbuid, opts.dbgid,
		    B_FALSE, cmd);
		mms_trace(MMS_DEBUG, "exec_mgmt_cmd: %s %s %s %s %s %s",
		    cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5]);

		st = check_exit(pid, NULL);
		if (st == 0) {
			break;
		}
		(void) sleep(1);
	}

	(void) fclose(readf);

	return (st);
}

int
mgmt_db_check(void)
{
	int		st;
	pid_t		pid;
	char		buf[1024];
	FILE		*readf = NULL;
	mmsdb_opts_t	opts;
	char		*cmd[9];
	char		dbbuf[2048];

	if (!mgmt_chk_auth("solaris.mms.modify")) {
		return (EACCES);
	}

	st = mgmt_get_db_opts(&opts);
	if (st != 0) {
		return (st);
	}

	/* test to see if the mms database already exists */

	(void) snprintf(dbbuf, sizeof (dbbuf), "%s/psql", opts.bindir);

	cmd[0] = dbbuf;
	cmd[1] = "-h";
	cmd[2] = opts.dbhost;
	cmd[3] = "-p";
	cmd[4] = opts.port;
	cmd[5] = "-t";
	cmd[6] = "-c";
	cmd[7] = buf;
	cmd[8] = NULL;

	(void) snprintf(buf, sizeof (buf),
	    "SELECT datname FROM pg_database where datname = '%s'",
	    opts.dbname);

	pid = exec_mgmt_cmd(&readf, NULL, opts.dbuid, opts.dbgid,
	    B_FALSE, cmd);
	mms_trace(MMS_DEBUG, "exec_mgmt_cmd: %s %s %s %s %s %s %s %s",
	    cmd[0], cmd[1], cmd[2], cmd[3], cmd[4],
	    cmd[5], cmd[6], cmd[7]);

	st = check_exit(pid, NULL);

	if (st == 0) {
		buf[0] = '\0';
		(void) fgets(buf, sizeof (buf), readf);
		if (buf[0] == '\0') {
			st = -1;
		}
	}

	(void) fclose(readf);

	return (st);
}

int
mgmt_db_dump(char *dumpdir, char *dumpfile, int len)
{
	int		st;
	char		datebuf[256];
	char		filbuf[MAXPATHLEN];
	time_t		now = time(NULL);
	struct tm	*tm = NULL;
	pid_t		pid;
	mmsdb_opts_t	opts;
	char		*cmd[11];
	char		dbbuf[2048];

	if (!dumpdir || !dumpfile) {
		return (MMS_MGMT_NOARG);
	}

	if (!mgmt_chk_auth("solaris.mms.modify")) {
		return (EACCES);
	}

	st = mgmt_get_db_opts(&opts);
	if (st != 0) {
		return (st);
	}

	tm = localtime(&now);
	(void) strftime(datebuf, sizeof (datebuf),
	    "%Y-""%m-""%dT""%H""%M""%S", tm);

	(void) snprintf(filbuf, sizeof (filbuf), "%s/mmsdb_dump_%s", dumpdir,
	    datebuf);

	(void) snprintf(dbbuf, sizeof (dbbuf), "%s/pg_dump", opts.bindir);

	cmd[0] = dbbuf;
	cmd[1] = "-h";
	cmd[2] = opts.dbhost;
	cmd[3] = "-p";
	cmd[4] = opts.port;
	cmd[5] = "-F";
	cmd[6] = "p";
	cmd[7] = "-f";
	cmd[8] = filbuf;
	cmd[9] = opts.dbname;
	cmd[10] = NULL;

	if (dumpfile != NULL) {
		(void) strlcpy(dumpfile, filbuf, len);
	}

	st = create_dir(dumpdir, 0711, NULL, opts.dbuid, NULL, 0);

	if (st != 0) {
		return (st);
	}

	pid = exec_mgmt_cmd(NULL, NULL, opts.dbuid, opts.dbgid,
	    B_FALSE, cmd);
	mms_trace(MMS_DEBUG, "exec_mgmt_cmd: %s %s %s %s %s %s %s %s %s %s",
	    cmd[0], cmd[1], cmd[2], cmd[3], cmd[4],
	    cmd[5], cmd[6], cmd[7], cmd[8], cmd[9]);

	st = check_exit(pid, NULL);

	return (st);
}


/*
 *  TODO:  Ensure this is done on the MM server host only when client
 *  configs are supported.
 */
int
mgmt_db_restore(char *dumpfile)
{
	int		st;
	struct stat64	statbuf;
	char		*mmstate = NULL;
	mmsdb_opts_t	opts;

	if (!mgmt_chk_auth("solaris.mms.modify")) {
		return (EACCES);
	}

	st = mgmt_get_db_opts(&opts);
	if (st != 0) {
		return (st);
	}

	st = stat64(dumpfile, &statbuf);
	if (st != 0) {
		return (MMS_MGMT_DBDUMP_MISSING);
	}
	if (!S_ISREG(statbuf.st_mode)) {
		return (MMS_MGMT_NOT_DBFILE);
	}

	/* shutdown MM */
	mms_trace(MMS_DEBUG, "disable mm");
	st = mgmt_set_svc_state(MMSVC, DISABLE, &mmstate);
	if (st != 0) {
		if (mmstate) {
			free(mmstate);
		}
		return (st);
	}

	st = mgmt_db_create(1, 0, NULL);
	if (st != 0) {
		free(mmstate);
		return (st);
	}

	st = mgmt_db_sql_exec(dumpfile, &opts);

	if ((st == 0) && (strcmp(mmstate, "online") == 0)) {
		mms_trace(MMS_DEBUG, "enable mm");
		st = mgmt_set_svc_state(MMSVC, ENABLE, NULL);
	}

	free(mmstate);
	return (st);
}

typedef struct {
	char	*optnam;
	char	*val;
} pgconf_t;

static pgconf_t pgconf_opts[] = {
	{"port", NULL},
	{"log_directory", NULL},
	{"external_pid_file", "'postgres.pid'"},
	{"log_destination", "'stderr'"},
	{"logging_collector", "on"},
	{"log_filename", "'log.%a'"},
	{"log_rotation_size", "10000"},
	{"log_truncate_on_rotation", "on"},
	{"log_line_prefix", "'%m %p '"},
	{"client_min_messages", "WARNING"},
	{"log_min_messages", "INFO"},
	{"log_disconnections", "on"},
	{"track_counts", "on"},
	{"autovacuum", "on"},
	{"autovacuum_naptime", "1200"}
};

static int numpgopts = sizeof (pgconf_opts) / sizeof (pgconf_t);

static int
configure_pgconf(
	char	*port,
	char	*logdir)
{
	int		st = 0;
	struct stat64	statbuf;
	char		nambuf[256];
	char		buf[MAXPATHLEN];
	char		dbpath[MAXPATHLEN];
	char		logpath[MAXPATHLEN];
	uid_t		uid;
	size_t		sz;
	char		*bufp;
	struct tm	usetime;
	int		infd;
	int		outfd;
	FILE		*infp;
	FILE		*outfp;
	static char	datefmt[] = "%y""%m""%d""%H""%M""%S";
	time_t		now;
	char		filbuf[2048];
	int		i;
	char		*cptr;
	int		changed = 0;
	int		matched = 0;

	if ((port == NULL) || (logdir == NULL)) {
		return (MMS_MGMT_NOARG);
	}

	pgconf_opts[0].val = port;

	/* add quotes around the path */
	(void) snprintf(logpath, sizeof (logpath), "'%s'", logdir);

	pgconf_opts[1].val = logpath;

	st = get_db_user(nambuf, sizeof (nambuf), &uid, NULL);
	if (st == 0) {
		st = mms_cfg_getvar(MMS_CFG_DB_DATA, dbpath);
	}

	if (st != 0) {
		return (st);
	}

	st = stat64(logdir, &statbuf);
	if (st != 0) {
		st = errno;
		if (st == ENOENT) {
			st = create_dir(logdir, 0711, NULL, uid, NULL, 0);
		}
		if (st != 0) {
			return (st);
		}
	} else {
		if (!S_ISDIR(statbuf.st_mode)) {
			return (ENOTDIR);
		}
	}

	sz = strlcat(dbpath, "/postgresql.conf", sizeof (dbpath));
	if (sz > sizeof (dbpath)) {
		return (ENAMETOOLONG);
	}

	st = stat64(dbpath, &statbuf);
	if (st != 0) {
		if (errno != ENOENT) {
			return (errno);
		} else {
			/*
			 * DB not initialized yet, bail without
			 * error as this function will be called
			 * later from db_create.
			 */
			return (0);
		}
	}

	/* construct the name of the new version of this file */
	now = time(NULL);
	(void) localtime_r(&now, &usetime);
	(void) strftime(nambuf, sizeof (nambuf), datefmt, &usetime);
	(void) snprintf(buf, sizeof (buf), "%s_%s", dbpath, nambuf);

	/* open the original */
	infd = open64(dbpath, O_RDONLY);
	if (infd == -1) {
		st = errno;
		return (st);
	}

	infp = fdopen(infd, "r");
	if (infp == NULL) {
		st = errno;
		(void) close(infd);
		return (st);
	}

	/* open the target file */
	outfd = open64(buf, O_CREAT|O_RDWR, statbuf.st_mode);
	if (outfd == -1) {
		st = errno;
		(void) close(infd);
		return (st);
	}

	outfp = fdopen(outfd, "w");
	if (outfp == NULL) {
		st = errno;
		(void) fclose(infp);
		(void) close(outfd);
		return (st);
	}

	/* preserve, as much as possible, the existing format of the file */
	while ((bufp = fgets(filbuf, sizeof (filbuf), infp)) != NULL) {
		matched = 0;
		cptr = NULL;

		for (; *bufp != '\0'; bufp++) {
			/* options are initially commented out */
			if (*bufp == '#') {
				continue;
			}
			if (!isspace(*bufp)) {
				break;
			}
		}

		if (*bufp == '\0') {
			(void) fprintf(outfp, "%s", filbuf);
			continue;
		}

		for (i = 0; i < numpgopts; i++) {
			sz = strlen(pgconf_opts[i].optnam);
			if (strncmp(bufp, pgconf_opts[i].optnam, sz) != 0) {
				continue;
			}
			bufp += sz;
			while ((*bufp != '\0') && (isspace(*bufp))) {
				bufp++;
				sz++;
			}
			if (*bufp == '=') {
				/* found a match - update it */
				matched++;

				/* TODO:  check really needed to change */
				(void) fprintf(outfp, "%s = %s\n",
				    pgconf_opts[i].optnam, pgconf_opts[i].val);
				cptr = strchr(bufp, '#');
				if (cptr) {
					(void) fprintf(outfp, "\t\t\t\t\t%s",
					    cptr);
				}
				changed++;
				break;
			} else {
				/* superstring or substring of another option */
				bufp -= sz;
			}
		}

		if (!matched) {
			(void) fprintf(outfp, "%s", filbuf);
		}
	}
	(void) fchown(outfd, statbuf.st_uid, statbuf.st_gid);
	(void) fclose(outfp);
	(void) fclose(infp);

	/* if we didn't change anything, we're done */
	if (!changed) {
		(void) unlink(buf);
		return (0);
	}

	/* construct the name of the backup copy of this file */
	(void) localtime_r(&(statbuf.st_mtime), &usetime);
	(void) strftime(nambuf, sizeof (nambuf), datefmt, &usetime);

	(void) snprintf(filbuf, sizeof (filbuf), "%s_%s", dbpath, nambuf);

	/* finally, swap em */
	st = rename(dbpath, filbuf);
	if (st != 0) {
		st = errno;
		(void) unlink(buf);
	} else {
		st = rename(buf, dbpath);
		if (st != 0) {
			st = errno;
		}
	}

	return (st);
}

static int
get_dbver_from_optfile(char *path, int *version)
{
	int			st = 0;
	FILE			*fp = NULL;
	char			buf[1024];
	char			*bufp;
	int			vers = -1;
	int			last = -1;

	if (!path || !version) {
		return (ENOENT);
	}

	st = access(path, R_OK);
	if (st != 0) {
		st = errno;
		return (st);
	}

	fp = fopen(path, "r");
	if (fp == NULL) {
		st = errno;
		return (errno);
	}

	while ((bufp = fgets(buf, sizeof (buf), fp)) != NULL) {
		if ((*bufp != '\0') && (!isdigit(*bufp))) {
			continue;
		}

		do {
			bufp++;
		} while (isdigit(*bufp));

		if (*bufp != 'u') {
			continue;
		}

		*bufp = '\0';

		vers = atoi(buf);
		if (vers > last) {
			last = vers;
		}
	}
	(void) fclose(fp);

	*version = last;

	return (st);
}

static int
mk_cmds_from_optfile(mmsdb_opts_t *opts, char *path, int vers, char cmdtype,
	int dopd, char **cmdfile)
{
	int			st = 0;
	int			fd = -1;
	FILE			*fp = NULL;
	FILE			*ofp = NULL;
	char			buf[MAXPATHLEN];
	char			*bufp;
	int			started = 0;
	char			*pass;

	if (!opts || !path || !cmdfile) {
		return (MMS_MGMT_NOARG);
	}

	st = access(path, R_OK);
	if (st != 0) {
		st = errno;
		return (st);
	}

	fp = fopen(path, "r");
	if (fp == NULL) {
		st = errno;
		return (errno);
	}

	/* create our cmdfile */
	(void) snprintf(buf, sizeof (buf), "%s/../mmsdbcmd-%c-%d",
	    opts->path, cmdtype, time(NULL));

	fd = open(buf, O_CREAT|O_TRUNC|O_APPEND|O_WRONLY|O_NOFOLLOW|O_NOLINKS,
	    0600);
	if (fd == -1) {
		st = errno;
		goto done;
	}

	/* set this so the pguser can read it */
	(void) fchown(fd, opts->dbuid, opts->dbgid);

	ofp = fdopen(fd, "a");
	if (ofp == NULL) {
		st = errno;
		(void) close(fd);
		goto done;
	}

	*cmdfile = strdup(buf);

	(void) fprintf(ofp, "BEGIN;\n");

	while ((bufp = fgets(buf, sizeof (buf), fp)) != NULL) {
		if (*bufp == '#') {
			continue;
		}

		if (!isdigit(*bufp)) {
			if (started) {
				(void) fprintf(ofp, "%s", bufp);
			}
			continue;
		}

		while (isdigit(*bufp)) {
			bufp++;
		}

		if (*bufp++ == cmdtype) {
			started = 1;
			while (isspace(*buf)) {
				bufp++;
			}
			(void) fprintf(ofp, "%s", bufp);
		} else {
			started = 0;
		}
	}

	if (dopd) {
		(void) fclose(fp);

		/* override the junk password in the dbopts file */
		pass = mms_net_cfg_read_pass_file(MMS_NET_CFG_HELLO_FILE);
		if (pass != NULL) {
			(void) fprintf(ofp,
			    "UPDATE \"MMPASSWORD\" SET \"Password\" = '%s' "
			    "WHERE \"ApplicationName\" = 'MMS';\n",
			    pass);
			free(pass);
		}
	}

	/* Set the version and add commit statement */
	(void) fprintf(ofp,
	    "UPDATE \"MM\" SET \"DBVersion\" = '%d';\nCOMMIT;\n", vers);

done:
	if (st != 0) {
		if (*cmdfile) {
			(void) unlink(*cmdfile);
			free(cmdfile);
			*cmdfile = NULL;
		}
	}
	(void) fclose(fp);
	(void) fclose(ofp);

	return (st);
}

static int
mgmt_db_sql_exec(char *cmdfile, mmsdb_opts_t *opts)
{
	int	st;
	pid_t	pid;
	FILE	*dberr;
	char	buf[MAXPATHLEN];
	char	*cmd[10];
	char	dbbuf[2048];

	if (!cmdfile || !opts) {
		return (MMS_MGMT_NOARG);
	}

	(void) snprintf(dbbuf, sizeof (dbbuf), "%s/psql", opts->bindir);

	cmd[0] = dbbuf;
	cmd[1] = "-a";
	cmd[2] = "-h";
	cmd[3] = opts->dbhost;
	cmd[4] = "-p";
	cmd[5] = opts->port;
	cmd[6] = "-f";
	cmd[7] = cmdfile;
	cmd[8] = opts->dbname;
	cmd[9] = NULL;

	pid = exec_mgmt_cmd(NULL, &dberr, opts->dbuid, opts->dbgid,
	    B_FALSE, cmd);
	mms_trace(MMS_DEBUG, "exec_mgmt_cmd: %s %s %s %s %s %s %s %s %s",
	    cmd[0], cmd[1], cmd[2], cmd[3], cmd[4],
	    cmd[5], cmd[6], cmd[7], cmd[8]);

	st = check_exit(pid, NULL);

	if (st == 0) {
		while (fgets(buf, sizeof (buf), dberr) != NULL) {
			if ((strstr(buf, " ERROR: ") != NULL) ||
			    (strstr(buf, "ROLLBACK") != NULL)) {
				st = 1;
				break;
			}
		}
	}
	(void) fclose(dberr);

	return (st);
}

/*
 * this should probably move to a header somewhere, but for
 * now, leave it where it's directly mapped
 */
typedef struct {
	char	*mm;
	char	*ui;
} mgmt_dbopt_map_t;

/*
 * map of MgmtUI-specified opts to MM options.  NULL indicates
 * not exposed by MgmtUI
 */
static mgmt_dbopt_map_t mm_sys_opts[] = {
	{"AttendanceMode", O_ATTENDED},
	{"SystemLogLevel", O_LOGLEVEL},
	{"SystemLogFile", O_LOGFILE},
	{"MessageLevel", O_MSGLEVEL},
	{"TraceLevel", O_TRACELEVEL},
	{"TraceFileSize", O_TRACESZ},
	{"SocketFdLimit", O_NUMSOCKET},
	{"SystemDiskMountTimeout", O_DKTIMEOUT},
	{"WatcherStartsLimit", O_NUMRESTART},
	{"SystemAcceptLevel", NULL},
	{"SystemMessageLimit", NULL},
	{"SystemMessageCount", NULL},
	{"SystemRequestLimit", NULL},
	{"SystemRequestCount", NULL},
	{"SystemSyncLimit", NULL},
	{"SystemDCALimit", NULL},
	{"SystemDCACount", NULL},
	{"ClearDriveAtLMConfig", NULL},
	{"AskClearDriveAtLMConfig", NULL},
	{"PreemptReservation", NULL},
	{"SystemLogFileSize", NULL},
	{"SystemName", NULL},
	{"SystemInstance", NULL},
	{"UnloadDelayTime", NULL},
	{"DefaultBlocksize", NULL},
	{"WatcherTimeLimit", NULL},
	{"DriveRecordRetention", NULL},
	{NULL, NULL}
};

/* add the MM options to the database before creation, if we know them */
static int
set_mm_system_vars_db(nvlist_t *opts, char *cmdfile)
{
	int		st;
	char		*val = NULL;
	int		i;
	int		fd = -1;
	FILE		*ofp = NULL;
	struct stat	sb;
	int		changed = 0;

	if (!opts) {
		return (0);
	}

	if (!cmdfile) {
		return (MMS_MGMT_NOARG);
	}

	fd = open(cmdfile, O_RDWR|O_NOFOLLOW|O_NOLINKS);
	if (fd == -1) {
		return (errno);
	}
	st = fstat(fd, &sb);
	if (st != 0) {
		st = errno;
		(void) close(fd);
		return (st);
	}

	ofp = fdopen(fd, "a");
	if (ofp == NULL) {
		st = errno;
		(void) close(fd);
		return (st);
	}

	for (i = 0; mm_sys_opts[i].mm != NULL; i++) {
		if (mm_sys_opts[i].ui == NULL) {
			continue;
		}
		st = nvlist_lookup_string(opts, mm_sys_opts[i].ui, &val);
		if ((st == 0) && val) {
			if (!changed) {
				(void) fprintf(ofp, "BEGIN;\n");
			}
			(void) fprintf(ofp,
			    "UPDATE \"SYSTEM\" SET \"%s\" = '%s';\n",
			    mm_sys_opts[i].mm, val);

			changed++;
		}
	}

	if (changed) {
		(void) fprintf(ofp, "COMMIT;\n");
	}

	(void) fclose(ofp);

	return (0);
}

int
mgmt_set_db_pass(char *dbpass, nvlist_t *errs)
{
	int		st;
	mmsdb_opts_t	opts;
	char		buf[2048];
	boolean_t	ismd5 = B_FALSE;
	int		fd = -1;
	int		wr = 0;
	char		file[MAXPATHLEN];

	/* no provided password means use 'trust' */
	if (dbpass) {
		ismd5 = B_TRUE;
	}

	st = mgmt_get_db_opts(&opts);
	if (st != 0) {
		return (st);
	}

	/* tell Postgres to use the new password */
	(void) snprintf(file, sizeof (file), "%s/../tsql", opts.path);
	fd = open64(file, O_CREAT|O_TRUNC|O_WRONLY, 0600);
	if (fd == -1) {
		st = errno;
		MGMT_ADD_ERR(errs, file, st);
		return (st);
	}

	(void) fchown(fd, opts.dbuid, opts.dbgid);

	(void) snprintf(buf, sizeof (buf),
	    "alter user postgres with password '%s' valid until 'infinity';",
	    dbpass);
	wr = write_buf(fd, buf, strlen(buf));
	(void) close(fd);

	if (wr == -1) {
		MGMT_ADD_ERR(errs, file, EIO);
		(void) unlink(file);
		return (EIO);
	}

	st = mgmt_db_sql_exec(file, &opts);
	if (st != 0) {
		MGMT_ADD_ERR(errs, "postgres failure", st);
		(void) unlink(file);
		return (st);
	}

	(void) unlink(file);

	/* next, set up the conf file */
	st = update_pghba(ismd5, &opts, errs);
	if (st != 0) {
		return (st);
	}

	/* write the PGPASSFILE */
	(void) snprintf(file, sizeof (file), "%s/../%s",
	    opts.path, MMS_MGMT_PGA);
	fd = open64(file, O_CREAT|O_TRUNC|O_WRONLY, 0600);
	if (fd == -1) {
		st = errno;
		MGMT_ADD_ERR(errs, file, st);
		return (st);
	}
	(void) fchown(fd, opts.dbuid, opts.dbgid);

	(void) snprintf(buf, sizeof (buf), "*:*:*:*:%s", dbpass);
	wr = write_buf(fd, buf, strlen(buf));
	(void) close(fd);

	if (wr == -1) {
		MGMT_ADD_ERR(errs, file, EIO);
		(void) unlink(file);
		return (EIO);
	}

	st = mms_net_cfg_write_pass_file(MMS_NET_CFG_DB_FILE, dbpass);
	if (st != 0) {
		return (st);
	}

	/* restart the db */
	mms_trace(MMS_DEBUG, "restart db");
	st = mgmt_set_svc_state(DBSVC, RESTART, NULL);

	if (st == 0) {
		st = mgmt_db_ready();
		if (st != 0) {
			mms_trace(MMS_ERR,
			    "database did not go ready");
		}
	}

	return (st);
}

static int
update_pghba(boolean_t ismd5, mmsdb_opts_t *dbopts, nvlist_t *errs)
{
	int		st;
	char		buf[2048];
	char		*bufp;
	int		infd = -1;
	int		outfd = -1;
	FILE		*infp = NULL;
	FILE		*outfp = NULL;
	struct stat64	statbuf;
	char		confpath[2048];
	char		newconfpath[2048];
	time_t		now;
	struct tm	usetime;
	static char	datefmt[] = "%y""%m""%d""%H""%M""%S";
	boolean_t	changed = B_FALSE;
	char		timebuf[256];

	if (!dbopts) {
		return (MMS_MGMT_NOARG);
	}

	(void) snprintf(confpath, sizeof (confpath), "%s/pg_hba.conf",
	    dbopts->path);

	st = stat64(confpath, &statbuf);
	if (st != 0) {
		st = errno;
		MGMT_ADD_ERR(errs, confpath, st);
		return (st);
	}

	/* construct the name of the new version of this file */
	now = time(NULL);
	(void) localtime_r(&now, &usetime);
	(void) strftime(timebuf, sizeof (timebuf), datefmt, &usetime);
	(void) snprintf(newconfpath, sizeof (newconfpath), "%s_%s", confpath,
	    timebuf);

	/* open the original */
	infd = open64(confpath, O_RDONLY);
	if (infd == -1) {
		st = errno;
		MGMT_ADD_ERR(errs, confpath, st);
		return (st);
	}

	infp = fdopen(infd, "r");
	if (infp == NULL) {
		st = errno;
		MGMT_ADD_ERR(errs, confpath, st);
		(void) close(infd);
		return (st);
	}

	/* open the target file */
	outfd = open64(newconfpath, O_CREAT|O_RDWR, 0600);
	if (outfd == -1) {
		st = errno;
		(void) close(infd);
		MGMT_ADD_ERR(errs, newconfpath, st);
		return (st);
	}

	outfp = fdopen(outfd, "w");
	if (outfp == NULL) {
		st = errno;
		(void) fclose(infp);
		(void) close(outfd);
		MGMT_ADD_ERR(errs, newconfpath, st);
		return (st);
	}

	/* preserve, as much as possible, the existing format of the file */
	while ((bufp = fgets(buf, sizeof (buf), infp)) != NULL) {
		while (isspace(*bufp)) {
			bufp++;
		}

		if ((*bufp == '\0') || (*bufp == '#')) {
			(void) fprintf(outfp, "%s", buf);
			continue;
		}

		/* look for 'trust' or 'md5' */
		if (ismd5) {
			/* changing to password-protected */
			bufp = strstr(buf, "trust");
			if (bufp) {
				(void) strlcpy(bufp, "md5\n", 7);
				changed = B_TRUE;
			}
		} else {
			/* removing password protection */
			bufp = strstr(buf, "md5");
			if (bufp) {
				(void) strlcpy(bufp, "trust\n", 7);
				changed = B_TRUE;
			}
		}

		(void) fprintf(outfp, "%s", buf);
	}

	(void) fchown(outfd, dbopts->dbuid, dbopts->dbgid);
	(void) fclose(outfp);
	(void) fclose(infp);

	/* if we didn't change anything, we're done */
	if (!changed) {
		(void) unlink(newconfpath);
		return (0);
	}

	/* construct the name of the backup copy of this file */
	(void) localtime_r(&(statbuf.st_mtime), &usetime);
	(void) strftime(timebuf, sizeof (timebuf), datefmt, &usetime);

	(void) snprintf(buf, sizeof (buf), "%s_%s", confpath, timebuf);

	/* finally, swap em */
	st = rename(confpath, buf);
	if (st != 0) {
		st = errno;
		(void) unlink(buf);
		MGMT_ADD_ERR(errs, confpath, st);
	} else {
		st = rename(newconfpath, confpath);
		if (st != 0) {
			st = errno;
			MGMT_ADD_ERR(errs, newconfpath, st);
		}
	}

	return (st);
}