OpenSolaris_b135/cmd/drd/drd.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.
 */

/*
 * sun4v DR daemon
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <fcntl.h>
#include <errno.h>
#include <libgen.h>
#include <syslog.h>
#include <door.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <sys/drctl_impl.h>
#include <sys/drctl.h>
#include "drd.h"

boolean_t drd_debug = B_FALSE;
boolean_t drd_daemonized = B_FALSE;

#define	DRD_DOOR_FILE		"/tmp/drd_door"
#define	DRD_DOOR_RETURN_ERR()	(void) door_return(NULL, 0, NULL, 0)

static char *cmdname;
static int drctl_fd;
static drctl_rsrc_t *drd_result = NULL;

/*
 * Currently, the only supported backend is for the Reconfiguration
 * Coordination Manager (RCM). When there are other backends, this
 * variable should be set dynamically.
 */
static drd_backend_t *drd_backend = &drd_rcm_backend;

static void drd_daemonize(void);
static int drd_init_drctl_dev(boolean_t standalone);
static int drd_init_door_server(boolean_t standalone);
static void drd_door_server(void *, char *, size_t, door_desc_t *, uint_t);

int
main(int argc, char **argv)
{
	int		opt;
	boolean_t	standalone = B_FALSE;

	cmdname = basename(argv[0]);

	/*
	 * Process command line arguments
	 */
	opterr = 0;	/* disable getopt error messages */
	while ((opt = getopt(argc, argv, "ds")) != EOF) {

		switch (opt) {
		case 'd':
			drd_debug = B_TRUE;
			break;
		case 's':
			standalone = B_TRUE;
			break;
		default:
			drd_err("unkown option: -%c", optopt);
			exit(1);
		}
	}

	drd_dbg("initializing %s...", cmdname);

	/* must be root */
	if (geteuid() != 0) {
		drd_err("permission denied: must run as root");
		exit(1);
	}

	/* open the drctl device */
	if (drd_init_drctl_dev(standalone) != 0) {
		drd_err("unable to initialize drctl device");
		exit(1);
	}

	/* daemonize */
	if (!standalone) {
		drd_daemonize();
	}

	/* initialize door server */
	if (drd_init_door_server(standalone) != 0) {
		drd_err("unable to initialize door server");
		exit(1);
	}

	/* initialize the backend */
	if ((*drd_backend->init)() != 0) {
		drd_err("unable to initialize backend processor");
		exit(1);
	}

	/* loop forever */
	for (;;) {
		pause();
	}

	/*NOTREACHED*/
	return (0);
}

static void
drd_daemonize(void)
{
	pid_t	pid;

	if ((pid = fork()) == -1) {
		drd_err("failed to fork: %s", strerror(errno));
		exit(1);
	}

	if (pid != 0) {
		/* parent */
		exit(0);
	}

	/*
	 * Initialize child process
	 */
	(void) setsid();
	(void) chdir("/");
	(void) umask(0);

	/*
	 * Initialize file descriptors. Do not touch stderr
	 * which is initialized by SMF to point to the drd
	 * specific log file.
	 */
	assert(drctl_fd == (STDERR_FILENO + 1));

	(void) close(STDIN_FILENO);
	(void) open("/dev/null", O_RDWR);
	(void) dup2(STDIN_FILENO, STDOUT_FILENO);

	closefrom(drctl_fd + 1);

	/* initialize logging */
	openlog(cmdname, LOG_CONS | LOG_NDELAY, LOG_DAEMON);

	drd_daemonized = B_TRUE;
}

static int
drd_init_drctl_dev(boolean_t standalone)
{
	void (*drd_output)(char *, ...);

	drd_output = (standalone) ? drd_info : drd_err;

	/* open the drctl device */
	if ((drctl_fd = open(DRCTL_DEV, O_RDWR)) == -1) {
		drd_output("open %s failed: %s", DRCTL_DEV, strerror(errno));
		return ((standalone) ? 0 : -1);
	}

	return (0);
}

static int
drd_init_door_server(boolean_t standalone)
{
	int		door_fd;
	int		dbg_fd;
	drctl_setup_t	setup;

	assert((drctl_fd != -1) || standalone);

	/* create the door */
	if ((door_fd = door_create(drd_door_server, NULL, 0)) == -1) {
		drd_err("door_create failed: %s", strerror(errno));
		return (-1);
	}

	if (drctl_fd != -1) {

		setup.did = door_fd;

		/* send the door descriptor to drctl */
		if (ioctl(drctl_fd, DRCTL_IOCTL_CONNECT_SERVER, &setup) == -1) {
			drd_err("drctl ioctl failed: %s", strerror(errno));
			(void) door_revoke(door_fd);
			return (-1);
		}

		drd_dbg("connection to drctl established");

		/* setup is complete in daemon mode */
		if (!standalone) {
			return (0);
		}
	}

	/*
	 * At this point, the daemon is running in standalone
	 * mode for testing purposes. This allows the daemon
	 * to be controlled directly through a door exported
	 * to the filesystem. No drctl device is required in
	 * this mode.
	 */

	/* create the door file */
	unlink(DRD_DOOR_FILE);
	if ((dbg_fd = creat(DRD_DOOR_FILE, 0644)) == -1) {
		drd_err("failed to create door file '%s': %s",
		    DRD_DOOR_FILE, strerror(errno));
		(void) door_revoke(door_fd);
		return (-1);
	}
	close(dbg_fd);

	/* attach the door file to the door descriptor */
	if (fattach(door_fd, DRD_DOOR_FILE) == -1) {
		drd_err("failed to fattach door file '%s': %s",
		    DRD_DOOR_FILE, strerror(errno));
		unlink(DRD_DOOR_FILE);
		(void) door_revoke(door_fd);
		return (-1);
	}

	drd_dbg("door server attached to '%s'", DRD_DOOR_FILE);

	return (0);
}

static size_t
drd_pack_response(drctl_rsrc_t *rsrcs, int nrsrc)
{
	drctl_rsrc_t	*orsrcsp;
	void		*resizep;
	size_t		osize;
	char		*str;
	size_t		offset;
	char		*off;
	int		idx;
	size_t		len;

	drd_dbg("drd_pack_response...");

	/*
	 * Deallocate the global response buffer if it is
	 * in use. This assumes that there will only ever
	 * be one pending operation in the daemon. This is
	 * enforced by the kernel.
	 */
	s_free(drd_result);

	orsrcsp = calloc(sizeof (*orsrcsp), nrsrc);
	osize = sizeof (*orsrcsp) * nrsrc;
	bcopy(rsrcs, orsrcsp, osize);

	offset = osize;

	/*
	 * Loop through all the resources and concatenate
	 * all the error strings to the end of the resource
	 * array. Also, update the offset field of each
	 * resource.
	 */
	for (idx = 0; idx < nrsrc; idx++) {

		str = (char *)(uintptr_t)rsrcs[idx].offset;

		/* skip if no error string */
		if (str == NULL)
			continue;

		len = strlen(str) + 1;

		/* increase the size of the buffer */
		resizep = realloc(orsrcsp, osize + len);
		if (resizep == NULL) {
			drd_err("realloc failed: %s", strerror(errno));
			s_free(orsrcsp);

			/* clean up any remaining strings */
			while (idx < nrsrc) {
				str = (char *)(uintptr_t)rsrcs[idx++].offset;
				s_free(str);
			}
			return (0);
		}

		orsrcsp = resizep;

		/* copy the error string into the response */
		off = (char *)orsrcsp + offset;
		bcopy(str, off, len);
		orsrcsp[idx].offset = offset;

		/*
		 * Now that the error string has been copied
		 * into the response message, the memory that
		 * was allocated for it is no longer needed.
		 */
		s_free(str);
		rsrcs[idx].offset = 0;

		/* update size and offset */
		offset += len;
		osize += len;
	}

	drd_result = orsrcsp;
	return (osize);
}

/*ARGSUSED*/
static void
drd_door_server(void *cookie, char *argp, size_t arg_sz, door_desc_t *dp,
    uint_t n_desc)
{
	drd_msg_t	*msg = (drd_msg_t *)(uintptr_t)argp;
	drctl_rsrc_t	*rsrcs;
	size_t		osize;
	int		nrsrc;

	drd_dbg("drd_door_server...");
	drd_dbg("message received: %d bytes", arg_sz);

	/* sanity check incoming arg */
	if ((argp == NULL) || (arg_sz == 0))
		DRD_DOOR_RETURN_ERR();

	drd_dbg("  cmd=%d, count=%d, flags=%d", msg->cmd,
	    msg->count, msg->flags);

	rsrcs = (drctl_rsrc_t *)(uintptr_t)msg->data;
	nrsrc = msg->count;

	/* pass off to backend for processing */
	switch (msg->cmd) {
	case DRCTL_CPU_CONFIG_REQUEST:
		(*drd_backend->cpu_config_request)(rsrcs, nrsrc);
		break;

	case DRCTL_CPU_CONFIG_NOTIFY:
		(*drd_backend->cpu_config_notify)(rsrcs, nrsrc);
		break;

	case DRCTL_CPU_UNCONFIG_REQUEST:
		(*drd_backend->cpu_unconfig_request)(rsrcs, nrsrc);
		break;

	case DRCTL_CPU_UNCONFIG_NOTIFY:
		(*drd_backend->cpu_unconfig_notify)(rsrcs, nrsrc);
		break;

	case DRCTL_MEM_CONFIG_REQUEST:
		(*drd_backend->mem_config_request)(rsrcs, nrsrc);
		break;

	case DRCTL_MEM_CONFIG_NOTIFY:
		(*drd_backend->mem_config_notify)(rsrcs, nrsrc);
		break;

	case DRCTL_MEM_UNCONFIG_REQUEST:
		(*drd_backend->mem_unconfig_request)(rsrcs, nrsrc);
		break;

	case DRCTL_MEM_UNCONFIG_NOTIFY:
		(*drd_backend->mem_unconfig_notify)(rsrcs, nrsrc);
		break;

	case DRCTL_IO_CONFIG_REQUEST:
		(*drd_backend->io_config_request)(rsrcs, nrsrc);
		break;

	case DRCTL_IO_CONFIG_NOTIFY:
		(*drd_backend->io_config_notify)(rsrcs, nrsrc);
		break;

	case DRCTL_IO_UNCONFIG_REQUEST:
		(*drd_backend->io_unconfig_request)(rsrcs, nrsrc);
		break;

	case DRCTL_IO_UNCONFIG_NOTIFY:
		(*drd_backend->io_unconfig_notify)(rsrcs, nrsrc);
		break;

	default:
		drd_err("unknown command: %d", msg->cmd);
		DRD_DOOR_RETURN_ERR();
		break;
	}

	osize = drd_pack_response(rsrcs, nrsrc);
	if (osize == 0)
		DRD_DOOR_RETURN_ERR();

	(void) door_return((char *)drd_result, osize, NULL, 0);
}