#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"
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];
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
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
boolean_t door_created = B_FALSE;
msg_queue_t *sys_q = NULL;
msg_queue_t *scn_q = NULL;
#ifdef DEBUG
extern void *cli_test(void *argv);
extern int dump_db(void);
#endif
extern void sigalrm(int);
static void
sigusr2_handler(
int sig
)
{
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);
}
static void
sigchld_handler(
int sig
)
{
int status;
pid_t ret_pid;
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);
}
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);
}
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(
int argc,
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;
}
#endif
openlog(ISNS_DAEMON_SYSLOG_PP, LOG_PID | LOG_CONS, LOG_DAEMON);
if (load_config(B_TRUE) != 0) {
isnslog(LOG_ERR, "main", "administrative settings load error.");
exit(SMF_EXIT_ERR_OTHER);
}
(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;
isns_child_pid = fork();
if (isns_child_pid < 0) {
exit(SMF_EXIT_ERR_CONFIG);
}
if (isns_child_pid > 0) {
(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);
}
i = open("/dev/null", O_RDWR);
(void) dup2(i, 1);
(void) dup2(i, 2);
}
#ifdef DEBUG
printf("calling cache init\n");
#endif
if (cache_init() != 0) {
isnslog(LOG_ERR, "main",
"object hash table initialization error.");
exit(SMF_EXIT_ERR_OTHER);
}
if (el_init(10, 60, 6) != 0) {
isnslog(LOG_ERR, "main",
"ESI event list initialization error.");
exit(SMF_EXIT_ERR_OTHER);
}
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
sys_q = queue_calloc();
if (!sys_q) {
exit(SMF_EXIT_ERR_OTHER);
}
scn_q = queue_calloc();
if (!scn_q) {
exit(SMF_EXIT_ERR_OTHER);
}
if (verify_ddd() != 0) {
exit(SMF_EXIT_ERR_OTHER);
}
if (verify_scn_portal() != 0) {
exit(SMF_EXIT_ERR_OTHER);
}
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);
if (pthread_create(&scn_tid, NULL, scn_proc, NULL) != 0) {
isnslog(LOG_ERR, "main", "SCN thread creating error.");
exit(SMF_EXIT_ERR_OTHER);
}
if (setup_mgmt_door(sys_q) != 0) {
exit(SMF_EXIT_ERR_OTHER);
}
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);
}
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) {
pthread_t tid;
(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);
}
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:
reg_expiring(msg->data);
break;
case DEAD_PORTAL:
portal_dies((uint32_t)msg->data);
break;
case SERVER_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);
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:
(void) load_config(B_FALSE);
break;
case SYS_QUIT_OK:
(void) queue_msg_free(msg);
exit(0);
default:
break;
}
(void) queue_msg_free(msg);
}
return (0);
}