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