OpenSolaris_b135/cmd/isns/isnsd/main.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 <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <errno.h>
#include <libscf.h>
#ifdef DEBUG
#include <time.h>
#endif
#include <signal.h>
#include <semaphore.h>
#include <sys/wait.h>

#include "isns_server.h"
#include "isns_dseng.h"
#include "isns_msgq.h"
#include "isns_log.h"
#include "isns_cfg.h"
#include "isns_utils.h"
#include "isns_cache.h"
#include "isns_obj.h"
#include "isns_dd.h"
#include "isns_scn.h"
#include "isns_sched.h"
#include "isns_esi.h"
#include "isns_mgmt.h"

/*
 * iSNS Server administrative settings.
 */
uint8_t daemonlize = 0;
int dbg_level = 7;
uint64_t esi_threshold;
uint8_t mgmt_scn;
ctrl_node_t *control_nodes = NULL;
pthread_mutex_t ctrl_node_mtx = PTHREAD_MUTEX_INITIALIZER;
char data_store[MAXPATHLEN];


/* semaphore for handling exit */
static sem_t	isns_child_sem;
static int	isns_child_smf_exit_code;
static pid_t	isns_child_pid;

#if !defined(SMF_EXIT_ERR_OTHER)
#define	SMF_EXIT_ERR_OTHER	-1
#endif

/*
 * Globals for singal handling.  time_to_exit is set by sig_handle()
 * when set the main thread(daemon) and othere threads should exit.
 *
 * semaphone is used to make sure all threads that are created
 * by isns_port_watcher and esi.
 */
boolean_t time_to_exit = B_FALSE;
static uint32_t thr_ref_count;
static pthread_mutex_t thr_count_mtx = PTHREAD_MUTEX_INITIALIZER;
#define	MAX_RETRY_COUNT	10 /* for checking remaining threads before exit. */

/*
 * Door creation flag.
 */
boolean_t door_created = B_FALSE;

/*
 * global system message queue
 */
msg_queue_t *sys_q = NULL;
msg_queue_t *scn_q = NULL;

#ifdef DEBUG
extern void *cli_test(void *argv);
extern dump_db(void);
#endif

extern void sigalrm(int);

/*
 * sigusr2_handler -- SIGUSR2 Handler
 * sigusr2 is exepected only when child is running okay.
 */
/* ARGSUSED */
static void
sigusr2_handler(
	int	sig
)
{
	/* post okay status. */
	isnslog(LOG_DEBUG, "sigusr2_handler",
	    "SIGUSR@ is received.  Parent is existing...");
	isns_child_smf_exit_code = SMF_EXIT_OK;

	(void) sem_post(&isns_child_sem);
}

/*
 * sigchld_handler -- SIGCHLD Handler
 * sigchld is exepected only when there is an error.
 */
/* ARGSUSED */
static void
sigchld_handler(
	int	sig
)
{
	int	status;
	pid_t	ret_pid;

	/* This is the default code. */
	isns_child_smf_exit_code = SMF_EXIT_ERR_OTHER;

	ret_pid = waitpid(isns_child_pid, &status, WNOHANG);

	if (ret_pid == isns_child_pid) {
		if (WIFEXITED(status)) {
			isns_child_smf_exit_code = WEXITSTATUS(status);
		}
	}
	(void) sem_post(&isns_child_sem);
}

/* ARGSUSED */
static void
sighup_handler(
	int	sig
)
{

	isnslog(LOG_DEBUG, "sighup_handle",
	    "SIGHUP is received.  Reloading config...");
	(void) queue_msg_set(sys_q, CONFIG_RELOAD, NULL);
}

/* ARGSUSED */
static void
sigexit_handler(
	int	sig
)
{
	isnslog(LOG_DEBUG, "sigexit_handler",
	    "Signal: %d received and sending server exit.", sig);
	shutdown_server();
}

void
inc_thr_count(
)
{
	(void) pthread_mutex_lock(&thr_count_mtx);

	isnslog(LOG_DEBUG, "inc_thr_count",
	    "increase thread reference count(%d).", thr_ref_count);

	thr_ref_count++;

	(void) pthread_mutex_unlock(&thr_count_mtx);
}

void
dec_thr_count(
)
{
	(void) pthread_mutex_lock(&thr_count_mtx);

	isnslog(LOG_DEBUG, "dec_thr_count",
	    "decrease thread reference count(%d).", thr_ref_count);

	thr_ref_count--;

	(void) pthread_mutex_unlock(&thr_count_mtx);
}

uint32_t
get_thr_count(
)
{
	uint32_t ref;

	(void) pthread_mutex_lock(&thr_count_mtx);

	ref = thr_ref_count;

	(void) pthread_mutex_unlock(&thr_count_mtx);

	isnslog(LOG_DEBUG, "get_thr_count",
	    "checking thread reference count %d.", ref);

	return (ref);
}

void
shutdown_server(
)
{
	isnslog(LOG_DEBUG, "shutdown", "raise exit flag.");
	time_to_exit = B_TRUE;
	(void) queue_msg_set(sys_q, SERVER_EXIT, NULL);
}

int
main(
	/* LINTED E_FUNC_ARG_UNUSED */
	int	argc,
	/* LINTED E_FUNC_ARG_UNUSED */
	char	*argv[]
)
{
	int opt_i = 0;
	pthread_t port_tid, esi_tid, scn_tid;
	uint32_t thr_cnt;
	int i;

#ifdef DEBUG
	time_t t;
	clock_t c;
#endif

#ifdef DEBUG
	if (getopt(argc, argv, "i") == 'i') {
		opt_i = 1; /* interactive mode */
	}
#endif

	/* set locale */
	openlog(ISNS_DAEMON_SYSLOG_PP, LOG_PID | LOG_CONS, LOG_DAEMON);

	/* load administative settings. pick up data location. */
	if (load_config(B_TRUE) != 0) {
		isnslog(LOG_ERR, "main", "administrative settings load error.");
		exit(SMF_EXIT_ERR_OTHER);
	}

	/* A signal handler is set for SIGCHLD. */
	(void) signal(SIGCHLD, sigchld_handler);
	(void) signal(SIGUSR2, sigusr2_handler);
	(void) sigset(SIGALRM, sigalrm);

#ifdef DEBUG
	printf("start daemon\n");
#endif
	if (opt_i == 0 || daemonlize) {
		isnslog(LOG_DEBUG, "main", "now forking... pid %d", getpid());
		daemonlize = 1;
		/* daemonlize */
		isns_child_pid = fork();
		if (isns_child_pid < 0) {
			/*
			 * cannot fork(), terminate the server.
			 */
			exit(SMF_EXIT_ERR_CONFIG);
		}
		if (isns_child_pid > 0) {
			/*
			 * terminate parent.
			 */
			(void) sem_wait(&isns_child_sem);
			(void) sem_destroy(&isns_child_sem);
			isnslog(LOG_DEBUG, "main", "exiting with %d",
				isns_child_smf_exit_code);
			exit(isns_child_smf_exit_code);
		}

		/*
		 * redirect stdout, and stderr to /dev/null.
		 */
		i = open("/dev/null", O_RDWR);
		(void) dup2(i, 1);
		(void) dup2(i, 2);
	} /* end of daemonlize */

#ifdef DEBUG
	printf("calling cache init\n");
#endif
	/* initialize object hash table */
	if (cache_init() != 0) {
		isnslog(LOG_ERR, "main",
		    "object hash table initialization error.");
		exit(SMF_EXIT_ERR_OTHER);
	}

	/* initialize event list */
	if (el_init(10, 60, 6) != 0) {
		isnslog(LOG_ERR, "main",
		"ESI event list initialization error.");
		exit(SMF_EXIT_ERR_OTHER);
	}

	/* initialize iSNS database */
	if (init_data() != 0) {
		isnslog(LOG_ERR, "main",
		    "internal database initialization error");
		exit(SMF_EXIT_ERR_OTHER);
	}

#ifdef DEBUG
	printf("calling load_data\n");
	t = time(NULL);
	c = clock();
#endif

	if (load_data() != 0) {
		isnslog(LOG_ERR, "main", "loading data store failed");
		exit(SMF_EXIT_ERR_OTHER);
	}

#ifdef DEBUG
	t = time(NULL) - t;
	c = clock() - c;
	printf("time %d clock %.4lf -loading data\n",
	    t, c / (double)CLOCKS_PER_SEC);
#endif

#ifdef DEBUG
	printf("sys queue creating...\n");
#endif
	/* create a message queue for system control */
	sys_q = queue_calloc();
	if (!sys_q) {
		exit(SMF_EXIT_ERR_OTHER);
	}

	/* create a message queue for scn thread */
	scn_q = queue_calloc();
	if (!scn_q) {
		exit(SMF_EXIT_ERR_OTHER);
	}

	/* create scn thread */
	/* Check for Default DD/DD-set existence and */
	/* create them if they are not there. */
	if (verify_ddd() != 0) {
		exit(SMF_EXIT_ERR_OTHER);
	}

	/* setup and verify the portal(s) for scn(s) */
	/* after scn registry is loaded from data store. */
	if (verify_scn_portal() != 0) {
		exit(SMF_EXIT_ERR_OTHER);
	}

	/* setup and verify the portal(s) for esi(s) */
	/* after esi list is loaded from data store. */
	if (verify_esi_portal() != 0) {
		exit(SMF_EXIT_ERR_OTHER);
	}

#ifdef DEBUG
	printf("scn queue creating...\n");
#endif

	(void) sigset(SIGHUP, sighup_handler);
	(void) sigset(SIGINT, sigexit_handler);
	(void) sigset(SIGTERM, sigexit_handler);
	(void) sigset(SIGQUIT, sigexit_handler);

	/* create scn thread */
	if (pthread_create(&scn_tid, NULL, scn_proc, NULL) != 0) {
		isnslog(LOG_ERR, "main", "SCN thread creating error.");
		exit(SMF_EXIT_ERR_OTHER);
	}

	/* setup a door for management interface */
	if (setup_mgmt_door(sys_q) != 0) {
		exit(SMF_EXIT_ERR_OTHER);
	}

	/* create server port watcher */
	if (pthread_create(&port_tid, NULL,
	    isns_port_watcher, (void *)sys_q) != 0) {
		isnslog(LOG_ERR, "main", "iSNS port thread creating error.");
		exit(SMF_EXIT_ERR_OTHER);
	}

	/* create entity status inquiry thread */
	if (pthread_create(&esi_tid, NULL,
	    esi_proc, NULL) != 0) {
		isnslog(LOG_ERR, "main", "ESI thread creating error.");
		exit(SMF_EXIT_ERR_OTHER);
	}

#ifdef DEBUG
	if (!daemonlize) {
		(void) pthread_create(&tid,
		    NULL,
		    cli_test,
		    (void *)sys_q);
	}
#endif
	if (opt_i == 0 || daemonlize) {
		isnslog(LOG_DEBUG, "main", "issuing SIGUSR2.. parent pid %d",
		    getppid());
		(void) kill(getppid(), SIGUSR2);
	}

	/* pause */
	for (;;) {
		msg_text_t *msg = queue_msg_get(sys_q);
		switch (msg->id) {
			case DATA_ADD:
			case DATA_UPDATE:
			case DATA_DELETE:
			case DATA_DELETE_ASSOC:
			case DATA_COMMIT:
			case DATA_RETREAT:
				break;
			case REG_EXP:
				/* registration expiring */
				reg_expiring(msg->data);
				break;
			case DEAD_PORTAL:
				portal_dies((uint32_t)msg->data);
				break;
			case SERVER_EXIT:
				/* graceful exit. */
				(void) queue_msg_free(msg);
				isnslog(LOG_DEBUG, "main",
				    "wake up ESI and stop it.");
				(void) get_stopwatch(1);
				isnslog(LOG_DEBUG, "main",
				    "sending SCN stop msg.");
				(void) queue_msg_set(scn_q, SCN_STOP, NULL);
				if (door_created) {
					isnslog(LOG_DEBUG, "main",
					    "closing the door.");
					(void) fdetach(ISNS_DOOR_NAME);
				}
				(void) pthread_join(esi_tid, NULL);
				isnslog(LOG_DEBUG, "main",
				    "esi thread %d exited.", esi_tid);
				(void) pthread_join(port_tid, NULL);
				isnslog(LOG_DEBUG, "main",
				    "port watcher thread %d exited.", port_tid);
				(void) pthread_join(scn_tid, NULL);
				isnslog(LOG_DEBUG, "main",
				    "scn thread %d exited.", scn_tid);

				/* now check any remaining threads. */
				i = 0;
				do {
					thr_cnt = get_thr_count();
					if (thr_cnt == 0) {
						isnslog(LOG_DEBUG, "main",
						    "main thread %d is done.",
						    pthread_self());
						exit(1);
					} else {
						(void) sleep(1);
						i++;
					}
				} while (MAX_RETRY_COUNT > i);
				isnslog(LOG_DEBUG, "main",
				    "main thread %d existing ...",
				    pthread_self());
				exit(1);
				break;
			case CONFIG_RELOAD:
				/* load config again. don't pick data store. */
				(void) load_config(B_FALSE);
				break;
			case SYS_QUIT_OK:
				(void) queue_msg_free(msg);
				exit(0);
			default:
				break;
		}
		(void) queue_msg_free(msg);
	}

	/* LINTED E_STMT_NOT_REACHED */
	return (0);
}