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
sigusr2_handler(int sig)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
sigchld_handler(int sig)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
sighup_handler(int sig)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
sigexit_handler(int sig)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
inc_thr_count()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
dec_thr_count()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
get_thr_count()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
shutdown_server()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
main(int argc,char * argv[])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