xref: /illumos-gate/usr/src/cmd/isns/isnsd/main.c (revision c9eab9d4e096bb9b983e9b007577edfa73c32eff)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <sys/types.h>
28 #include <unistd.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <sys/stat.h>
32 #include <fcntl.h>
33 #include <pthread.h>
34 #include <errno.h>
35 #include <libscf.h>
36 #ifdef DEBUG
37 #include <time.h>
38 #endif
39 #include <signal.h>
40 #include <semaphore.h>
41 #include <sys/wait.h>
42 
43 #include "isns_server.h"
44 #include "isns_dseng.h"
45 #include "isns_msgq.h"
46 #include "isns_log.h"
47 #include "isns_cfg.h"
48 #include "isns_utils.h"
49 #include "isns_cache.h"
50 #include "isns_obj.h"
51 #include "isns_dd.h"
52 #include "isns_scn.h"
53 #include "isns_sched.h"
54 #include "isns_esi.h"
55 #include "isns_mgmt.h"
56 
57 /*
58  * iSNS Server administrative settings.
59  */
60 uint8_t daemonlize = 0;
61 int dbg_level = 7;
62 uint64_t esi_threshold;
63 uint8_t mgmt_scn;
64 ctrl_node_t *control_nodes = NULL;
65 pthread_mutex_t ctrl_node_mtx = PTHREAD_MUTEX_INITIALIZER;
66 char data_store[MAXPATHLEN];
67 
68 
69 /* semaphore for handling exit */
70 static sem_t	isns_child_sem;
71 static int	isns_child_smf_exit_code;
72 static pid_t	isns_child_pid;
73 
74 #if !defined(SMF_EXIT_ERR_OTHER)
75 #define	SMF_EXIT_ERR_OTHER	-1
76 #endif
77 
78 /*
79  * Globals for singal handling.  time_to_exit is set by sig_handle()
80  * when set the main thread(daemon) and othere threads should exit.
81  *
82  * semaphone is used to make sure all threads that are created
83  * by isns_port_watcher and esi.
84  */
85 boolean_t time_to_exit = B_FALSE;
86 static uint32_t thr_ref_count;
87 static pthread_mutex_t thr_count_mtx = PTHREAD_MUTEX_INITIALIZER;
88 #define	MAX_RETRY_COUNT	10 /* for checking remaining threads before exit. */
89 
90 /*
91  * global system message queue
92  */
93 msg_queue_t *sys_q = NULL;
94 msg_queue_t *scn_q = NULL;
95 
96 #ifdef DEBUG
97 extern void *cli_test(void *argv);
98 extern dump_db(void);
99 #endif
100 
101 extern void sigalrm(int);
102 
103 /*
104  * sigusr2_handler -- SIGUSR2 Handler
105  * sigusr2 is exepected only when child is running okay.
106  */
107 /* ARGSUSED */
108 static void
109 sigusr2_handler(
110 	int	sig
111 )
112 {
113 	/* post okay status. */
114 	isnslog(LOG_DEBUG, "sigusr2_handler",
115 	    "SIGUSR@ is received.  Parent is existing...");
116 	isns_child_smf_exit_code = SMF_EXIT_OK;
117 
118 	(void) sem_post(&isns_child_sem);
119 }
120 
121 /*
122  * sigchld_handler -- SIGCHLD Handler
123  * sigchld is exepected only when there is an error.
124  */
125 /* ARGSUSED */
126 static void
127 sigchld_handler(
128 	int	sig
129 )
130 {
131 	int	status;
132 	pid_t	ret_pid;
133 
134 	/* This is the default code. */
135 	isns_child_smf_exit_code = SMF_EXIT_ERR_OTHER;
136 
137 	ret_pid = waitpid(isns_child_pid, &status, WNOHANG);
138 
139 	if (ret_pid == isns_child_pid) {
140 		if (WIFEXITED(status)) {
141 			isns_child_smf_exit_code = WEXITSTATUS(status);
142 		}
143 	}
144 	(void) sem_post(&isns_child_sem);
145 }
146 
147 /* ARGSUSED */
148 static void
149 sighup_handler(
150 	int	sig
151 )
152 {
153 
154 	isnslog(LOG_DEBUG, "sighup_handle",
155 	    "SIGHUP is received.  Reloading config...");
156 	(void) queue_msg_set(sys_q, CONFIG_RELOAD, NULL);
157 }
158 
159 /* ARGSUSED */
160 static void
161 sigexit_handler(
162 	int	sig
163 )
164 {
165 	isnslog(LOG_DEBUG, "sigexit_handler",
166 	    "Signal: %d received and sending server exit.", sig);
167 	shutdown_server();
168 }
169 
170 void
171 inc_thr_count(
172 )
173 {
174 	(void) pthread_mutex_lock(&thr_count_mtx);
175 
176 	isnslog(LOG_DEBUG, "inc_thr_count",
177 	    "increase thread reference count(%d).", thr_ref_count);
178 
179 	thr_ref_count++;
180 
181 	(void) pthread_mutex_unlock(&thr_count_mtx);
182 }
183 
184 void
185 dec_thr_count(
186 )
187 {
188 	(void) pthread_mutex_lock(&thr_count_mtx);
189 
190 	isnslog(LOG_DEBUG, "dec_thr_count",
191 	    "decrease thread reference count(%d).", thr_ref_count);
192 
193 	thr_ref_count--;
194 
195 	(void) pthread_mutex_unlock(&thr_count_mtx);
196 }
197 
198 uint32_t
199 get_thr_count(
200 )
201 {
202 	uint32_t ref;
203 
204 	(void) pthread_mutex_lock(&thr_count_mtx);
205 
206 	ref = thr_ref_count;
207 
208 	(void) pthread_mutex_unlock(&thr_count_mtx);
209 
210 	isnslog(LOG_DEBUG, "get_thr_count",
211 	    "checking thread reference count %d.", ref);
212 
213 	return (ref);
214 }
215 
216 void
217 shutdown_server(
218 )
219 {
220 	isnslog(LOG_DEBUG, "shutdown", "raise exit flag.");
221 	time_to_exit = B_TRUE;
222 	(void) queue_msg_set(sys_q, SERVER_EXIT, NULL);
223 }
224 
225 int
226 main(
227 	/* LINTED E_FUNC_ARG_UNUSED */
228 	int	argc,
229 	/* LINTED E_FUNC_ARG_UNUSED */
230 	char	*argv[]
231 )
232 {
233 	int opt_i = 0;
234 	pthread_t port_tid, esi_tid, scn_tid;
235 	uint32_t thr_cnt;
236 	int i;
237 
238 #ifdef DEBUG
239 	time_t t;
240 	clock_t c;
241 #endif
242 
243 #ifdef DEBUG
244 	if (getopt(argc, argv, "i") == 'i') {
245 		opt_i = 1; /* interactive mode */
246 	}
247 #endif
248 
249 	/* set locale */
250 	openlog(ISNS_DAEMON_SYSLOG_PP, LOG_PID | LOG_CONS, LOG_DAEMON);
251 
252 	/* load administative settings. pick up data location. */
253 	if (load_config(B_TRUE) != 0) {
254 		isnslog(LOG_ERR, "main", "administrative settings load error.");
255 		exit(SMF_EXIT_ERR_OTHER);
256 	}
257 
258 	/* A signal handler is set for SIGCHLD. */
259 	(void) signal(SIGCHLD, sigchld_handler);
260 	(void) signal(SIGUSR2, sigusr2_handler);
261 	(void) sigset(SIGALRM, sigalrm);
262 
263 #ifdef DEBUG
264 	printf("start daemon\n");
265 #endif
266 	if (opt_i == 0 || daemonlize) {
267 		isnslog(LOG_DEBUG, "main", "now forking... pid %d", getpid());
268 		daemonlize = 1;
269 		/* daemonlize */
270 		isns_child_pid = fork();
271 		if (isns_child_pid < 0) {
272 			/*
273 			 * cannot fork(), terminate the server.
274 			 */
275 			exit(SMF_EXIT_ERR_CONFIG);
276 		}
277 		if (isns_child_pid > 0) {
278 			/*
279 			 * terminate parent.
280 			 */
281 			(void) sem_wait(&isns_child_sem);
282 			(void) sem_destroy(&isns_child_sem);
283 			isnslog(LOG_DEBUG, "main", "exiting with %d",
284 				isns_child_smf_exit_code);
285 			exit(isns_child_smf_exit_code);
286 		}
287 
288 		/*
289 		 * redirect stdout, and stderr to /dev/null.
290 		 */
291 		i = open("/dev/null", O_RDWR);
292 		(void) dup2(i, 1);
293 		(void) dup2(i, 2);
294 	} /* end of daemonlize */
295 
296 #ifdef DEBUG
297 	printf("calling cache init\n");
298 #endif
299 	/* initialize object hash table */
300 	if (cache_init() != 0) {
301 		isnslog(LOG_ERR, "main",
302 		    "object hash table initialization error.");
303 		exit(SMF_EXIT_ERR_OTHER);
304 	}
305 
306 	/* initialize event list */
307 	if (el_init(10, 60, 6) != 0) {
308 		isnslog(LOG_ERR, "main",
309 		"ESI event list initialization error.");
310 		exit(SMF_EXIT_ERR_OTHER);
311 	}
312 
313 	/* initialize iSNS database */
314 	if (init_data() != 0) {
315 		isnslog(LOG_ERR, "main",
316 		    "internal database initialization error");
317 		exit(SMF_EXIT_ERR_OTHER);
318 	}
319 
320 #ifdef DEBUG
321 	printf("calling load_data\n");
322 	t = time(NULL);
323 	c = clock();
324 #endif
325 
326 	if (load_data() != 0) {
327 		isnslog(LOG_ERR, "main", "loading data store failed");
328 		exit(SMF_EXIT_ERR_OTHER);
329 	}
330 
331 #ifdef DEBUG
332 	t = time(NULL) - t;
333 	c = clock() - c;
334 	printf("time %d clock %.4lf -loading data\n",
335 	    t, c / (double)CLOCKS_PER_SEC);
336 #endif
337 
338 #ifdef DEBUG
339 	printf("sys queue creating...\n");
340 #endif
341 	/* create a message queue for system control */
342 	sys_q = queue_calloc();
343 	if (!sys_q) {
344 		exit(SMF_EXIT_ERR_OTHER);
345 	}
346 
347 	/* create a message queue for scn thread */
348 	scn_q = queue_calloc();
349 	if (!scn_q) {
350 		exit(SMF_EXIT_ERR_OTHER);
351 	}
352 
353 	/* create scn thread */
354 	/* Check for Default DD/DD-set existence and */
355 	/* create them if they are not there. */
356 	if (verify_ddd() != 0) {
357 		exit(SMF_EXIT_ERR_OTHER);
358 	}
359 
360 	/* setup and verify the portal(s) for scn(s) */
361 	/* after scn registry is loaded from data store. */
362 	if (verify_scn_portal() != 0) {
363 		exit(SMF_EXIT_ERR_OTHER);
364 	}
365 
366 	/* setup and verify the portal(s) for esi(s) */
367 	/* after esi list is loaded from data store. */
368 	if (verify_esi_portal() != 0) {
369 		exit(SMF_EXIT_ERR_OTHER);
370 	}
371 
372 #ifdef DEBUG
373 	printf("scn queue creating...\n");
374 #endif
375 
376 	(void) sigset(SIGHUP, sighup_handler);
377 	(void) sigset(SIGINT, sigexit_handler);
378 	(void) sigset(SIGTERM, sigexit_handler);
379 	(void) sigset(SIGQUIT, sigexit_handler);
380 
381 	/* create scn thread */
382 	if (pthread_create(&scn_tid, NULL, scn_proc, NULL) != 0) {
383 		isnslog(LOG_ERR, "main", "SCN thread creating error.");
384 		exit(SMF_EXIT_ERR_OTHER);
385 	}
386 
387 	/* setup a door for management interface */
388 	if (setup_mgmt_door(sys_q) != 0) {
389 		exit(SMF_EXIT_ERR_OTHER);
390 	}
391 
392 	/* create server port watcher */
393 	if (pthread_create(&port_tid, NULL,
394 	    isns_port_watcher, (void *)sys_q) != 0) {
395 		isnslog(LOG_ERR, "main", "iSNS port thread creating error.");
396 		exit(SMF_EXIT_ERR_OTHER);
397 	}
398 
399 	/* create entity status inquiry thread */
400 	if (pthread_create(&esi_tid, NULL,
401 	    esi_proc, NULL) != 0) {
402 		isnslog(LOG_ERR, "main", "ESI thread creating error.");
403 		exit(SMF_EXIT_ERR_OTHER);
404 	}
405 
406 #ifdef DEBUG
407 	if (!daemonlize) {
408 		(void) pthread_create(&tid,
409 		    NULL,
410 		    cli_test,
411 		    (void *)sys_q);
412 	}
413 #endif
414 	if (opt_i == 0 || daemonlize) {
415 		isnslog(LOG_DEBUG, "main", "issuing SIGUSR2.. parent pid %d",
416 		    getppid());
417 		(void) kill(getppid(), SIGUSR2);
418 	}
419 
420 	/* pause */
421 	for (;;) {
422 		msg_text_t *msg = queue_msg_get(sys_q);
423 		switch (msg->id) {
424 			case DATA_ADD:
425 			case DATA_UPDATE:
426 			case DATA_DELETE:
427 			case DATA_DELETE_ASSOC:
428 			case DATA_COMMIT:
429 			case DATA_RETREAT:
430 				break;
431 			case REG_EXP:
432 				/* registration expiring */
433 				reg_expiring(msg->data);
434 				break;
435 			case DEAD_PORTAL:
436 				portal_dies((uint32_t)msg->data);
437 				break;
438 			case SERVER_EXIT:
439 				/* graceful exit. */
440 				(void) queue_msg_free(msg);
441 				isnslog(LOG_DEBUG, "main",
442 				    "wake up ESI and stop it.");
443 				(void) get_stopwatch(1);
444 				isnslog(LOG_DEBUG, "main",
445 				    "sending SCN stop msg.");
446 				(void) queue_msg_set(scn_q, SCN_STOP, NULL);
447 				isnslog(LOG_DEBUG, "main", "closing the door.");
448 				(void) fdetach(ISNS_DOOR_NAME);
449 				(void) pthread_join(esi_tid, NULL);
450 				isnslog(LOG_DEBUG, "main",
451 				    "esi thread %d exited.", esi_tid);
452 				(void) pthread_join(port_tid, NULL);
453 				isnslog(LOG_DEBUG, "main",
454 				    "port watcher thread %d exited.", port_tid);
455 				(void) pthread_join(scn_tid, NULL);
456 				isnslog(LOG_DEBUG, "main",
457 				    "scn thread %d exited.", scn_tid);
458 
459 				/* now check any remaining threads. */
460 				i = 0;
461 				do {
462 					thr_cnt = get_thr_count();
463 					if (thr_cnt == 0) {
464 						isnslog(LOG_DEBUG, "main",
465 						    "main thread %d is done.",
466 						    pthread_self());
467 						exit(1);
468 					} else {
469 						(void) sleep(1);
470 						i++;
471 					}
472 				} while (MAX_RETRY_COUNT > i);
473 				isnslog(LOG_DEBUG, "main",
474 				    "main thread %d existing ...",
475 				    pthread_self());
476 				exit(1);
477 				break;
478 			case CONFIG_RELOAD:
479 				/* load config again. don't pick data store. */
480 				(void) load_config(B_FALSE);
481 				break;
482 			case SYS_QUIT_OK:
483 				(void) queue_msg_free(msg);
484 				exit(0);
485 			default:
486 				break;
487 		}
488 		(void) queue_msg_free(msg);
489 	}
490 
491 	/* LINTED E_STMT_NOT_REACHED */
492 	return (0);
493 }
494