xref: /illumos-gate/usr/src/cmd/isns/isnsd/main.c (revision b8767451d156f585534afac0bf22721810d0dc63)
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 2009 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  * Door creation flag.
92  */
93 boolean_t door_created = B_FALSE;
94 
95 /*
96  * global system message queue
97  */
98 msg_queue_t *sys_q = NULL;
99 msg_queue_t *scn_q = NULL;
100 
101 #ifdef DEBUG
102 extern void *cli_test(void *argv);
103 extern dump_db(void);
104 #endif
105 
106 extern void sigalrm(int);
107 
108 /*
109  * sigusr2_handler -- SIGUSR2 Handler
110  * sigusr2 is exepected only when child is running okay.
111  */
112 /* ARGSUSED */
113 static void
114 sigusr2_handler(
115 	int	sig
116 )
117 {
118 	/* post okay status. */
119 	isnslog(LOG_DEBUG, "sigusr2_handler",
120 	    "SIGUSR@ is received.  Parent is existing...");
121 	isns_child_smf_exit_code = SMF_EXIT_OK;
122 
123 	(void) sem_post(&isns_child_sem);
124 }
125 
126 /*
127  * sigchld_handler -- SIGCHLD Handler
128  * sigchld is exepected only when there is an error.
129  */
130 /* ARGSUSED */
131 static void
132 sigchld_handler(
133 	int	sig
134 )
135 {
136 	int	status;
137 	pid_t	ret_pid;
138 
139 	/* This is the default code. */
140 	isns_child_smf_exit_code = SMF_EXIT_ERR_OTHER;
141 
142 	ret_pid = waitpid(isns_child_pid, &status, WNOHANG);
143 
144 	if (ret_pid == isns_child_pid) {
145 		if (WIFEXITED(status)) {
146 			isns_child_smf_exit_code = WEXITSTATUS(status);
147 		}
148 	}
149 	(void) sem_post(&isns_child_sem);
150 }
151 
152 /* ARGSUSED */
153 static void
154 sighup_handler(
155 	int	sig
156 )
157 {
158 
159 	isnslog(LOG_DEBUG, "sighup_handle",
160 	    "SIGHUP is received.  Reloading config...");
161 	(void) queue_msg_set(sys_q, CONFIG_RELOAD, NULL);
162 }
163 
164 /* ARGSUSED */
165 static void
166 sigexit_handler(
167 	int	sig
168 )
169 {
170 	isnslog(LOG_DEBUG, "sigexit_handler",
171 	    "Signal: %d received and sending server exit.", sig);
172 	shutdown_server();
173 }
174 
175 void
176 inc_thr_count(
177 )
178 {
179 	(void) pthread_mutex_lock(&thr_count_mtx);
180 
181 	isnslog(LOG_DEBUG, "inc_thr_count",
182 	    "increase thread reference count(%d).", thr_ref_count);
183 
184 	thr_ref_count++;
185 
186 	(void) pthread_mutex_unlock(&thr_count_mtx);
187 }
188 
189 void
190 dec_thr_count(
191 )
192 {
193 	(void) pthread_mutex_lock(&thr_count_mtx);
194 
195 	isnslog(LOG_DEBUG, "dec_thr_count",
196 	    "decrease thread reference count(%d).", thr_ref_count);
197 
198 	thr_ref_count--;
199 
200 	(void) pthread_mutex_unlock(&thr_count_mtx);
201 }
202 
203 uint32_t
204 get_thr_count(
205 )
206 {
207 	uint32_t ref;
208 
209 	(void) pthread_mutex_lock(&thr_count_mtx);
210 
211 	ref = thr_ref_count;
212 
213 	(void) pthread_mutex_unlock(&thr_count_mtx);
214 
215 	isnslog(LOG_DEBUG, "get_thr_count",
216 	    "checking thread reference count %d.", ref);
217 
218 	return (ref);
219 }
220 
221 void
222 shutdown_server(
223 )
224 {
225 	isnslog(LOG_DEBUG, "shutdown", "raise exit flag.");
226 	time_to_exit = B_TRUE;
227 	(void) queue_msg_set(sys_q, SERVER_EXIT, NULL);
228 }
229 
230 int
231 main(
232 	/* LINTED E_FUNC_ARG_UNUSED */
233 	int	argc,
234 	/* LINTED E_FUNC_ARG_UNUSED */
235 	char	*argv[]
236 )
237 {
238 	int opt_i = 0;
239 	pthread_t port_tid, esi_tid, scn_tid;
240 	uint32_t thr_cnt;
241 	int i;
242 
243 #ifdef DEBUG
244 	time_t t;
245 	clock_t c;
246 #endif
247 
248 #ifdef DEBUG
249 	if (getopt(argc, argv, "i") == 'i') {
250 		opt_i = 1; /* interactive mode */
251 	}
252 #endif
253 
254 	/* set locale */
255 	openlog(ISNS_DAEMON_SYSLOG_PP, LOG_PID | LOG_CONS, LOG_DAEMON);
256 
257 	/* load administative settings. pick up data location. */
258 	if (load_config(B_TRUE) != 0) {
259 		isnslog(LOG_ERR, "main", "administrative settings load error.");
260 		exit(SMF_EXIT_ERR_OTHER);
261 	}
262 
263 	/* A signal handler is set for SIGCHLD. */
264 	(void) signal(SIGCHLD, sigchld_handler);
265 	(void) signal(SIGUSR2, sigusr2_handler);
266 	(void) sigset(SIGALRM, sigalrm);
267 
268 #ifdef DEBUG
269 	printf("start daemon\n");
270 #endif
271 	if (opt_i == 0 || daemonlize) {
272 		isnslog(LOG_DEBUG, "main", "now forking... pid %d", getpid());
273 		daemonlize = 1;
274 		/* daemonlize */
275 		isns_child_pid = fork();
276 		if (isns_child_pid < 0) {
277 			/*
278 			 * cannot fork(), terminate the server.
279 			 */
280 			exit(SMF_EXIT_ERR_CONFIG);
281 		}
282 		if (isns_child_pid > 0) {
283 			/*
284 			 * terminate parent.
285 			 */
286 			(void) sem_wait(&isns_child_sem);
287 			(void) sem_destroy(&isns_child_sem);
288 			isnslog(LOG_DEBUG, "main", "exiting with %d",
289 				isns_child_smf_exit_code);
290 			exit(isns_child_smf_exit_code);
291 		}
292 
293 		/*
294 		 * redirect stdout, and stderr to /dev/null.
295 		 */
296 		i = open("/dev/null", O_RDWR);
297 		(void) dup2(i, 1);
298 		(void) dup2(i, 2);
299 	} /* end of daemonlize */
300 
301 #ifdef DEBUG
302 	printf("calling cache init\n");
303 #endif
304 	/* initialize object hash table */
305 	if (cache_init() != 0) {
306 		isnslog(LOG_ERR, "main",
307 		    "object hash table initialization error.");
308 		exit(SMF_EXIT_ERR_OTHER);
309 	}
310 
311 	/* initialize event list */
312 	if (el_init(10, 60, 6) != 0) {
313 		isnslog(LOG_ERR, "main",
314 		"ESI event list initialization error.");
315 		exit(SMF_EXIT_ERR_OTHER);
316 	}
317 
318 	/* initialize iSNS database */
319 	if (init_data() != 0) {
320 		isnslog(LOG_ERR, "main",
321 		    "internal database initialization error");
322 		exit(SMF_EXIT_ERR_OTHER);
323 	}
324 
325 #ifdef DEBUG
326 	printf("calling load_data\n");
327 	t = time(NULL);
328 	c = clock();
329 #endif
330 
331 	if (load_data() != 0) {
332 		isnslog(LOG_ERR, "main", "loading data store failed");
333 		exit(SMF_EXIT_ERR_OTHER);
334 	}
335 
336 #ifdef DEBUG
337 	t = time(NULL) - t;
338 	c = clock() - c;
339 	printf("time %d clock %.4lf -loading data\n",
340 	    t, c / (double)CLOCKS_PER_SEC);
341 #endif
342 
343 #ifdef DEBUG
344 	printf("sys queue creating...\n");
345 #endif
346 	/* create a message queue for system control */
347 	sys_q = queue_calloc();
348 	if (!sys_q) {
349 		exit(SMF_EXIT_ERR_OTHER);
350 	}
351 
352 	/* create a message queue for scn thread */
353 	scn_q = queue_calloc();
354 	if (!scn_q) {
355 		exit(SMF_EXIT_ERR_OTHER);
356 	}
357 
358 	/* create scn thread */
359 	/* Check for Default DD/DD-set existence and */
360 	/* create them if they are not there. */
361 	if (verify_ddd() != 0) {
362 		exit(SMF_EXIT_ERR_OTHER);
363 	}
364 
365 	/* setup and verify the portal(s) for scn(s) */
366 	/* after scn registry is loaded from data store. */
367 	if (verify_scn_portal() != 0) {
368 		exit(SMF_EXIT_ERR_OTHER);
369 	}
370 
371 	/* setup and verify the portal(s) for esi(s) */
372 	/* after esi list is loaded from data store. */
373 	if (verify_esi_portal() != 0) {
374 		exit(SMF_EXIT_ERR_OTHER);
375 	}
376 
377 #ifdef DEBUG
378 	printf("scn queue creating...\n");
379 #endif
380 
381 	(void) sigset(SIGHUP, sighup_handler);
382 	(void) sigset(SIGINT, sigexit_handler);
383 	(void) sigset(SIGTERM, sigexit_handler);
384 	(void) sigset(SIGQUIT, sigexit_handler);
385 
386 	/* create scn thread */
387 	if (pthread_create(&scn_tid, NULL, scn_proc, NULL) != 0) {
388 		isnslog(LOG_ERR, "main", "SCN thread creating error.");
389 		exit(SMF_EXIT_ERR_OTHER);
390 	}
391 
392 	/* setup a door for management interface */
393 	if (setup_mgmt_door(sys_q) != 0) {
394 		exit(SMF_EXIT_ERR_OTHER);
395 	}
396 
397 	/* create server port watcher */
398 	if (pthread_create(&port_tid, NULL,
399 	    isns_port_watcher, (void *)sys_q) != 0) {
400 		isnslog(LOG_ERR, "main", "iSNS port thread creating error.");
401 		exit(SMF_EXIT_ERR_OTHER);
402 	}
403 
404 	/* create entity status inquiry thread */
405 	if (pthread_create(&esi_tid, NULL,
406 	    esi_proc, NULL) != 0) {
407 		isnslog(LOG_ERR, "main", "ESI thread creating error.");
408 		exit(SMF_EXIT_ERR_OTHER);
409 	}
410 
411 #ifdef DEBUG
412 	if (!daemonlize) {
413 		(void) pthread_create(&tid,
414 		    NULL,
415 		    cli_test,
416 		    (void *)sys_q);
417 	}
418 #endif
419 	if (opt_i == 0 || daemonlize) {
420 		isnslog(LOG_DEBUG, "main", "issuing SIGUSR2.. parent pid %d",
421 		    getppid());
422 		(void) kill(getppid(), SIGUSR2);
423 	}
424 
425 	/* pause */
426 	for (;;) {
427 		msg_text_t *msg = queue_msg_get(sys_q);
428 		switch (msg->id) {
429 			case DATA_ADD:
430 			case DATA_UPDATE:
431 			case DATA_DELETE:
432 			case DATA_DELETE_ASSOC:
433 			case DATA_COMMIT:
434 			case DATA_RETREAT:
435 				break;
436 			case REG_EXP:
437 				/* registration expiring */
438 				reg_expiring(msg->data);
439 				break;
440 			case DEAD_PORTAL:
441 				portal_dies((uint32_t)msg->data);
442 				break;
443 			case SERVER_EXIT:
444 				/* graceful exit. */
445 				(void) queue_msg_free(msg);
446 				isnslog(LOG_DEBUG, "main",
447 				    "wake up ESI and stop it.");
448 				(void) get_stopwatch(1);
449 				isnslog(LOG_DEBUG, "main",
450 				    "sending SCN stop msg.");
451 				(void) queue_msg_set(scn_q, SCN_STOP, NULL);
452 				if (door_created) {
453 					isnslog(LOG_DEBUG, "main",
454 					    "closing the door.");
455 					(void) fdetach(ISNS_DOOR_NAME);
456 				}
457 				(void) pthread_join(esi_tid, NULL);
458 				isnslog(LOG_DEBUG, "main",
459 				    "esi thread %d exited.", esi_tid);
460 				(void) pthread_join(port_tid, NULL);
461 				isnslog(LOG_DEBUG, "main",
462 				    "port watcher thread %d exited.", port_tid);
463 				(void) pthread_join(scn_tid, NULL);
464 				isnslog(LOG_DEBUG, "main",
465 				    "scn thread %d exited.", scn_tid);
466 
467 				/* now check any remaining threads. */
468 				i = 0;
469 				do {
470 					thr_cnt = get_thr_count();
471 					if (thr_cnt == 0) {
472 						isnslog(LOG_DEBUG, "main",
473 						    "main thread %d is done.",
474 						    pthread_self());
475 						exit(1);
476 					} else {
477 						(void) sleep(1);
478 						i++;
479 					}
480 				} while (MAX_RETRY_COUNT > i);
481 				isnslog(LOG_DEBUG, "main",
482 				    "main thread %d existing ...",
483 				    pthread_self());
484 				exit(1);
485 				break;
486 			case CONFIG_RELOAD:
487 				/* load config again. don't pick data store. */
488 				(void) load_config(B_FALSE);
489 				break;
490 			case SYS_QUIT_OK:
491 				(void) queue_msg_free(msg);
492 				exit(0);
493 			default:
494 				break;
495 		}
496 		(void) queue_msg_free(msg);
497 	}
498 
499 	/* LINTED E_STMT_NOT_REACHED */
500 	return (0);
501 }
502