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 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 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 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 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 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 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 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 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 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