/*
 * 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 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <strings.h>
#include <fcntl.h>
#include <unistd.h>
#include <libscf.h>
#include <libscf_priv.h>
#include <libuutil.h>
#include "rcapd.h"
#include "rcapd_conf.h"
#include "rcapd_stat.h"
#include "utils.h"

/*
 * Read configuration and set the fields of an rcfg_t correspondingly.
 * Verify that the statistics file is writable, with the optional
 * verify_stat_file_creation() callback.
 */
int
rcfg_read(rcfg_t *_rcfg, int(*verify_stat_file_creation)(void))
{
	scf_simple_handle_t	*simple_h;
	uint64_t		count_val;
	int			ret = E_ERROR;

	rcfg_init(_rcfg);

	if ((simple_h = scf_general_pg_setup(RCAP_FMRI, CONFIG_PG))
	    == NULL) {
		warn(gettext("SMF initialization problem: %s\n"),
		    scf_strerror(scf_error()));
		goto err;
	}

	if (scf_read_count_property(simple_h, PRESSURE, &count_val)
	    == SCF_FAILED) {
		warn(gettext("Configuration property '%s' "
		    "not found. \n"), PRESSURE);
		goto err;
	} else {
		if (count_val > 100)
			_rcfg->rcfg_memory_cap_enforcement_pressure = 100;
		else
			_rcfg->rcfg_memory_cap_enforcement_pressure
			    = count_val;

		debug("cap max pressure: %d%%\n",
		    _rcfg->rcfg_memory_cap_enforcement_pressure);
	}

	if (scf_read_count_property(simple_h, RECONFIG_INT, &count_val)
	    == SCF_FAILED) {
		warn(gettext("Configuration property '%s' "
		    "not found. \n"), RECONFIG_INT);
		goto err;
	} else {
		_rcfg->rcfg_reconfiguration_interval = count_val;
		debug("reconfiguration interval: %d seconds\n",
		    _rcfg->rcfg_reconfiguration_interval);
	}

	if (scf_read_count_property(simple_h, REPORT_INT, &count_val)
	    == SCF_FAILED) {
		warn(gettext("Configuration property '%s' "
		    "not found. \n"), REPORT_INT);
		goto err;
	} else {
		_rcfg->rcfg_report_interval = count_val;
		debug("report interval: %d seconds\n",
		    _rcfg->rcfg_report_interval);
	}

	if (scf_read_count_property(simple_h, RSS_SAMPLE_INT, &count_val)
	    == SCF_FAILED) {
		warn(gettext("Configuration property '%s' "
		    "not found. \n"), RSS_SAMPLE_INT);
		goto err;
	} else {
		_rcfg->rcfg_rss_sample_interval = count_val;
		debug("RSS sample interval: %d seconds\n",
		    _rcfg->rcfg_rss_sample_interval);
	}

	if (scf_read_count_property(simple_h, WALK_INT, &count_val)
	    == SCF_FAILED) {
		warn(gettext("Configuration property '%s' "
		    "not found. \n"), WALK_INT);
		goto err;
	} else {
		_rcfg->rcfg_proc_walk_interval = count_val;
		debug("proc_walk interval: %d seconds\n",
		    _rcfg->rcfg_proc_walk_interval);
	}

	if (_rcfg->rcfg_mode_name == NULL) {
		/*
		 * Set project mode, by default.
		 */
		_rcfg->rcfg_mode = rctype_project;
		_rcfg->rcfg_mode_name = "project";
		debug("mode: %s\n", _rcfg->rcfg_mode_name);
	}

	if (verify_stat_file_creation != 0 && verify_stat_file_creation()
	    != 0) {
		warn(gettext("cannot create statistics file, " "%s"),
		    _rcfg->rcfg_stat_file);
		goto err;
	}

	debug("done parsing\n");
	ret = E_SUCCESS;
	goto out;

err:
	if (scf_error() != SCF_ERROR_NONE) {
		warn(gettext("Unexpected libscf error: %s. \n"),
		    scf_strerror(scf_error()));
	}

out:
	scf_simple_handle_destroy(simple_h);
	return (ret);
}

void
rcfg_init(rcfg_t *rcfg)
{
	bzero(rcfg, sizeof (*rcfg));
	(void) strcpy(rcfg->rcfg_stat_file, STAT_FILE_DEFAULT);
}

/*
 * Modify configuration in repository given the rcfg_t structure.
 */
int
modify_config(rcfg_t *conf)
{
	scf_simple_handle_t	*simple_h;
	scf_transaction_t	*tx = NULL;
	int			rval, ret = E_ERROR;

	if ((simple_h = scf_general_pg_setup(RCAP_FMRI, CONFIG_PG))
	    == NULL) {
		warn(gettext("SMF initialization problem: %s\n"),
		    scf_strerror(scf_error()));
		goto out;
	}

	if ((tx = scf_transaction_setup(simple_h)) == NULL) {
		warn(gettext("SMF initialization problem: %s\n"),
		    scf_strerror(scf_error()));
		goto out;
	}

	do {
		if (scf_set_count_property(tx, PRESSURE,
		    conf->rcfg_memory_cap_enforcement_pressure, 0)
		    != SCF_SUCCESS) {
			warn(gettext("Couldn't set '%s' property. \n"),
			    PRESSURE);
			goto out;
		}

		if (scf_set_count_property(tx, RECONFIG_INT,
		    conf->rcfg_reconfiguration_interval, 0) != SCF_SUCCESS) {
			warn(gettext("Couldn't set '%s' property. \n"),
			    RECONFIG_INT);
			goto out;
		}

		if (scf_set_count_property(tx, RSS_SAMPLE_INT,
		    conf->rcfg_rss_sample_interval, 0) != SCF_SUCCESS) {
			warn(gettext("Couldn't set '%s' property. \n"),
			    RSS_SAMPLE_INT);
			goto out;
		}

		if (scf_set_count_property(tx, REPORT_INT,
		    conf->rcfg_report_interval, 0) != SCF_SUCCESS) {
			warn(gettext("Couldn't set '%s' property. \n"),
			    REPORT_INT);
			goto out;
		}

		if (scf_set_count_property(tx, WALK_INT,
		    conf->rcfg_proc_walk_interval, 0) != SCF_SUCCESS) {
			warn(gettext("Couldn't set '%s' property. \n"),
			    WALK_INT);
			goto out;
		}

		if ((rval = scf_transaction_commit(tx)) == -1)
			goto out;

		if (rval == 0) {
			if (scf_transaction_restart(simple_h, tx)
			    != SCF_SUCCESS) {
				warn(gettext("SMF initialization problem: "
				    "%s\n"), scf_strerror(scf_error()));
				goto out;
			}
		}
	} while (rval == 0);

	ret = E_SUCCESS;

out:
	if (tx != NULL) {
		scf_transaction_destroy_children(tx);
		scf_transaction_destroy(tx);
	}
	scf_simple_handle_destroy(simple_h);
	return (ret);
}