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 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * vs_eng.c manages the vs_engines array of scan engine. 30 * Access to the array and other private data is protected by vs_eng_mutex. 31 * A caller can wait for an available engine connection on vs_eng_cv 32 * 33 */ 34 35 #include <sys/types.h> 36 #include <sys/synch.h> 37 #include <sys/socket.h> 38 #include <sys/filio.h> 39 #include <sys/ioctl.h> 40 #include <sys/debug.h> 41 #include <netinet/in.h> 42 #include <netinet/tcp.h> 43 #include <arpa/inet.h> 44 #include <unistd.h> 45 #include <stdlib.h> 46 #include <string.h> 47 #include <syslog.h> 48 #include <errno.h> 49 #include <poll.h> 50 #include <pthread.h> 51 #include <time.h> 52 53 #include "vs_incl.h" 54 55 56 typedef struct vs_engine { 57 vs_props_se_t vse_cfg; /* host, port, maxcon */ 58 int vse_in_use; /* # connections in use */ 59 int vse_error; 60 vs_eng_conn_t vse_conn_root; 61 } vs_engine_t; 62 63 static vs_engine_t vs_engines[VS_SE_MAX]; 64 65 static int vs_eng_next; /* round-robin "finger" */ 66 static int vs_eng_count; /* how many configured engines */ 67 static int vs_eng_total_maxcon; /* total configured connections */ 68 static int vs_eng_total_inuse; /* total connections in use */ 69 static int vs_eng_wait_count; /* # threads waiting for connection */ 70 71 static pthread_mutex_t vs_eng_mutex = PTHREAD_MUTEX_INITIALIZER; 72 static pthread_cond_t vs_eng_cv; 73 static pthread_cond_t vs_eng_shutdown_cv; 74 75 static time_t vs_eng_wait = VS_ENG_WAIT_DFLT; 76 77 /* local functions */ 78 static int vs_eng_check_errors(void); 79 static int vs_eng_find_next(int); 80 static void vs_eng_add_connection(vs_eng_conn_t *); 81 static void vs_eng_remove_connection(vs_eng_conn_t *); 82 static void vs_eng_close_connections(void); 83 static int vs_eng_compare(int, char *, int); 84 85 86 #ifdef FIONBIO 87 /* non-blocking connect */ 88 static int nbio_connect(vs_eng_conn_t *, const struct sockaddr *, int); 89 int vs_connect_timeout = 5000; /* milliseconds */ 90 #endif /* FIONBIO */ 91 92 93 /* 94 * vs_eng_init 95 */ 96 void 97 vs_eng_init() 98 { 99 (void) pthread_cond_init(&vs_eng_cv, NULL); 100 (void) pthread_cond_init(&vs_eng_shutdown_cv, NULL); 101 (void) pthread_mutex_lock(&vs_eng_mutex); 102 103 (void) memset(vs_engines, 0, 104 sizeof (vs_engine_t) * VS_SE_MAX); 105 vs_eng_total_maxcon = 0; 106 vs_eng_total_inuse = 0; 107 vs_eng_count = 0; 108 vs_eng_next = 0; 109 110 (void) pthread_mutex_unlock(&vs_eng_mutex); 111 } 112 113 114 /* 115 * vs_eng_config 116 * 117 * Configure scan engine connections. 118 * 119 * The enable property is the single indicator that the engine 120 * configuration is valid. Configuration is guaranteed (by the 121 * library) to be consistent; if the host is invalid or unconfigured, 122 * the enable setting will always be false. 123 * 124 * If a scan engine has been reconfigured (different host or port) 125 * the scan engine's error count and statitics are reset, and 126 * vs_icap_config is invoked to reset engine-specific data stored 127 * in vs_icap 128 * 129 */ 130 void 131 vs_eng_config(vs_props_all_t *config) 132 { 133 int i; 134 vs_props_se_t *cfg; 135 136 (void) pthread_mutex_lock(&vs_eng_mutex); 137 138 vs_eng_count = 0; 139 vs_eng_total_maxcon = 0; 140 141 for (i = 0; i < VS_SE_MAX; i++) { 142 cfg = &config->va_se[i]; 143 144 if (vs_eng_compare(i, cfg->vep_host, cfg->vep_port) != 0) { 145 vs_engines[i].vse_error = 0; 146 vs_icap_config(i, cfg->vep_host, cfg->vep_port); 147 } 148 149 if (cfg->vep_enable) { 150 vs_engines[i].vse_cfg = *cfg; 151 vs_eng_total_maxcon += cfg->vep_maxconn; 152 vs_eng_count++; 153 } else { 154 (void) memset(&vs_engines[i].vse_cfg, 0, 155 sizeof (vs_props_se_t)); 156 } 157 158 } 159 160 if ((vs_eng_total_maxcon <= 0) || (vs_eng_count == 0)) 161 syslog(LOG_WARNING, "Scan Engine - no engines configured"); 162 163 (void) pthread_mutex_unlock(&vs_eng_mutex); 164 } 165 166 167 /* 168 * vs_eng_fini 169 * 170 * Close all scan engine connections to abort in-progress scans, 171 * and wait until all to sessions are complete, and there are no 172 * waiting threads. 173 * Set vs_eng_total_maxcon to 0 to ensure no new engine sessions 174 * can be initiated while we're waiting. 175 */ 176 void 177 vs_eng_fini() 178 { 179 (void) pthread_mutex_lock(&vs_eng_mutex); 180 181 vs_eng_total_maxcon = 0; 182 183 vs_eng_close_connections(); 184 185 while (vs_eng_total_inuse > 0 || vs_eng_wait_count > 0) 186 (void) pthread_cond_wait(&vs_eng_shutdown_cv, &vs_eng_mutex); 187 188 (void) pthread_mutex_unlock(&vs_eng_mutex); 189 (void) pthread_cond_destroy(&vs_eng_cv); 190 (void) pthread_cond_destroy(&vs_eng_shutdown_cv); 191 } 192 193 194 /* 195 * vs_eng_set_error 196 * 197 * If the engine identified in conn (host, port) matches the 198 * engine in vs_engines set or clear the error state of the 199 * engine and update the error statistics. 200 * 201 * If error == 0, clear the error state(0), else set the error 202 * state (1) 203 */ 204 void 205 vs_eng_set_error(vs_eng_conn_t *conn, int error) 206 { 207 int idx = conn->vsc_idx; 208 209 (void) pthread_mutex_lock(&vs_eng_mutex); 210 211 if (vs_eng_compare(idx, conn->vsc_host, conn->vsc_port) == 0) 212 vs_engines[idx].vse_error = error ? 1 : 0; 213 214 (void) pthread_mutex_unlock(&vs_eng_mutex); 215 } 216 217 218 219 /* 220 * vs_eng_get 221 * Get next available scan engine connection. 222 * If retry != 0 look for a scan engine with no errors. 223 * 224 * Returns: 0 - success 225 * -1 - error 226 */ 227 int 228 vs_eng_get(vs_eng_conn_t *conn, int retry) 229 { 230 struct timespec tswait; 231 int idx; 232 233 (void) pthread_mutex_lock(&vs_eng_mutex); 234 235 /* 236 * If no engines connections configured or 237 * retry and only one engine configured, give up 238 */ 239 if ((vs_eng_total_maxcon <= 0) || (retry && (vs_eng_count <= 1))) { 240 (void) pthread_mutex_unlock(&vs_eng_mutex); 241 return (-1); 242 } 243 244 tswait.tv_sec = vs_eng_wait; 245 tswait.tv_nsec = 0; 246 247 while ((vscand_get_state() != VS_STATE_SHUTDOWN) && 248 ((idx = vs_eng_find_next(retry)) == -1)) { 249 /* If retry and all configured engines have errors, give up */ 250 if (retry && vs_eng_check_errors()) { 251 (void) pthread_mutex_unlock(&vs_eng_mutex); 252 return (-1); 253 } 254 255 /* wait for a connection to become available */ 256 vs_eng_wait_count++; 257 if (pthread_cond_reltimedwait_np(&vs_eng_cv, &vs_eng_mutex, 258 &tswait) < 0) { 259 syslog(LOG_WARNING, "Scan Engine " 260 "- timeout waiting for available engine"); 261 vs_eng_wait_count--; 262 if (vscand_get_state() == VS_STATE_SHUTDOWN) 263 (void) pthread_cond_signal(&vs_eng_shutdown_cv); 264 (void) pthread_mutex_unlock(&vs_eng_mutex); 265 return (-1); 266 } 267 vs_eng_wait_count--; 268 } 269 270 if (vscand_get_state() == VS_STATE_SHUTDOWN) { 271 (void) pthread_cond_signal(&vs_eng_shutdown_cv); 272 (void) pthread_mutex_unlock(&vs_eng_mutex); 273 return (-1); 274 } 275 276 conn->vsc_idx = idx; 277 (void) strlcpy(conn->vsc_engid, vs_engines[idx].vse_cfg.vep_engid, 278 sizeof (conn->vsc_engid)); 279 (void) strlcpy(conn->vsc_host, vs_engines[idx].vse_cfg.vep_host, 280 sizeof (conn->vsc_host)); 281 conn->vsc_port = vs_engines[idx].vse_cfg.vep_port; 282 283 /* update in use counts */ 284 vs_engines[idx].vse_in_use++; 285 vs_eng_total_inuse++; 286 287 /* add to connections list for engine */ 288 vs_eng_add_connection(conn); 289 290 /* update round-robin index */ 291 if (!retry) 292 vs_eng_next = (idx == VS_SE_MAX) ? 0 : idx + 1; 293 294 (void) pthread_mutex_unlock(&vs_eng_mutex); 295 296 return (0); 297 } 298 299 300 /* 301 * vs_eng_check_errors 302 * 303 * Check if there are any engines, with maxcon > 0, 304 * which are not in error state 305 * 306 * Returns: 1 - all (valid) engines are in error state 307 * 0 - otherwise 308 */ 309 static int 310 vs_eng_check_errors() 311 { 312 int i; 313 314 for (i = 0; i < VS_SE_MAX; i++) { 315 if (vs_engines[i].vse_cfg.vep_maxconn > 0 && 316 (vs_engines[i].vse_error == 0)) 317 return (0); 318 } 319 320 return (1); 321 } 322 323 324 /* 325 * vs_eng_find_next 326 * 327 * Returns: -1 no engine connections available 328 * idx of engine to use 329 */ 330 static int 331 vs_eng_find_next(int retry) 332 { 333 int i; 334 335 for (i = vs_eng_next; i < VS_SE_MAX; i++) { 336 if (vs_engines[i].vse_in_use < 337 vs_engines[i].vse_cfg.vep_maxconn) { 338 if (!retry || (vs_engines[i].vse_error == 0)) 339 return (i); 340 } 341 } 342 343 for (i = 0; i < vs_eng_next; i++) { 344 if (vs_engines[i].vse_in_use < 345 vs_engines[i].vse_cfg.vep_maxconn) { 346 if (!retry || (vs_engines[i].vse_error == 0)) 347 return (i); 348 } 349 } 350 351 return (-1); 352 } 353 354 355 /* 356 * vs_eng_release 357 */ 358 void 359 vs_eng_release(vs_eng_conn_t *conn) 360 { 361 int idx = conn->vsc_idx; 362 363 /* disconnect */ 364 if (conn->vsc_sockfd != -1) { 365 (void) close(conn->vsc_sockfd); 366 conn->vsc_sockfd = -1; 367 } 368 369 (void) pthread_mutex_lock(&vs_eng_mutex); 370 371 /* decrement in use counts */ 372 vs_engines[idx].vse_in_use--; 373 vs_eng_total_inuse--; 374 375 /* remove from connections list for engine */ 376 vs_eng_remove_connection(conn); 377 378 /* wake up next thread waiting for a connection */ 379 (void) pthread_cond_signal(&vs_eng_cv); 380 381 /* if shutdown, send shutdown signal */ 382 if (vscand_get_state() == VS_STATE_SHUTDOWN) 383 (void) pthread_cond_signal(&vs_eng_shutdown_cv); 384 385 (void) pthread_mutex_unlock(&vs_eng_mutex); 386 } 387 388 389 /* 390 * vs_eng_add_connection 391 * Add a connection into appropriate engine's connections list 392 */ 393 static void 394 vs_eng_add_connection(vs_eng_conn_t *conn) 395 { 396 vs_eng_conn_t *conn_root; 397 398 conn_root = &(vs_engines[conn->vsc_idx].vse_conn_root); 399 conn->vsc_prev = conn_root; 400 conn->vsc_next = conn_root->vsc_next; 401 if (conn->vsc_next) 402 (conn->vsc_next)->vsc_prev = conn; 403 conn_root->vsc_next = conn; 404 } 405 406 407 /* 408 * vs_eng_remove_connection 409 * Remove a connection from appropriate engine's connections list 410 */ 411 static void 412 vs_eng_remove_connection(vs_eng_conn_t *conn) 413 { 414 (conn->vsc_prev)->vsc_next = conn->vsc_next; 415 if (conn->vsc_next) 416 (conn->vsc_next)->vsc_prev = conn->vsc_prev; 417 } 418 419 420 /* 421 * vs_eng_close_connections 422 * Close all open connections to abort in-progress scans. 423 */ 424 static void 425 vs_eng_close_connections(void) 426 { 427 int i; 428 vs_eng_conn_t *conn; 429 430 for (i = 0; i < VS_SE_MAX; i++) { 431 conn = vs_engines[i].vse_conn_root.vsc_next; 432 while (conn) { 433 (void) close(conn->vsc_sockfd); 434 conn->vsc_sockfd = -1; 435 conn = conn->vsc_next; 436 } 437 } 438 } 439 440 441 /* 442 * vs_eng_connect 443 * open socket connection to remote scan engine 444 */ 445 int 446 vs_eng_connect(vs_eng_conn_t *conn) 447 { 448 int rc, sock_opt, err_num; 449 struct sockaddr_in addr; 450 struct hostent *hp; 451 452 if ((conn->vsc_sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) 453 return (-1); 454 455 hp = getipnodebyname(conn->vsc_host, AF_INET, 0, &err_num); 456 if (hp == NULL) 457 return (-1); 458 459 (void) memset(&addr, 0, sizeof (addr)); 460 (void) memcpy(&addr.sin_addr, hp->h_addr, hp->h_length); 461 addr.sin_port = htons(conn->vsc_port); 462 addr.sin_family = hp->h_addrtype; 463 freehostent(hp); 464 465 #ifdef FIONBIO /* Use non-blocking mode for connect. */ 466 rc = nbio_connect(conn, (struct sockaddr *)&addr, 467 sizeof (struct sockaddr)); 468 #else 469 rc = connect(conn->vsc_sockfd, (struct sockaddr *)&addr, 470 sizeof (struct sockaddr)); 471 #endif 472 473 sock_opt = 1; 474 475 if ((rc < 0) || (vscand_get_state() == VS_STATE_SHUTDOWN) || 476 (setsockopt(conn->vsc_sockfd, IPPROTO_TCP, TCP_NODELAY, 477 &sock_opt, sizeof (sock_opt)) < 0) || 478 (setsockopt(conn->vsc_sockfd, SOL_SOCKET, SO_KEEPALIVE, 479 &sock_opt, sizeof (sock_opt)) < 0)) { 480 (void) close(conn->vsc_sockfd); 481 conn->vsc_sockfd = -1; 482 return (-1); 483 } 484 485 return (0); 486 } 487 488 489 /* 490 * nbio_connect 491 * 492 * Attempt to do a non-blocking connect call. 493 * Wait for a maximum of "vs_connect_timeout" millisec, then check for 494 * socket error to determine if connect successful or not. 495 */ 496 #ifdef FIONBIO 497 static int 498 nbio_connect(vs_eng_conn_t *conn, const struct sockaddr *sa, int sa_len) 499 { 500 struct pollfd pfd; 501 int nbio, rc; 502 int soc = conn->vsc_sockfd; 503 int error, len = sizeof (error); 504 505 nbio = 1; 506 if ((ioctl(soc, FIONBIO, &nbio)) < 0) 507 return (connect(soc, sa, sa_len)); 508 509 if ((rc = connect(soc, sa, sa_len)) != 0) { 510 if (errno == EINPROGRESS || errno == EINTR) { 511 pfd.fd = soc; 512 pfd.events = POLLOUT; 513 pfd.revents = 0; 514 515 if ((rc = poll(&pfd, 1, vs_connect_timeout)) <= 0) { 516 if (rc == 0) 517 errno = ETIMEDOUT; 518 rc = -1; 519 } else { 520 rc = getsockopt(soc, SOL_SOCKET, SO_ERROR, 521 &error, &len); 522 if (rc != 0 || error != 0) 523 rc = -1; 524 } 525 } 526 } 527 528 nbio = 0; 529 (void) ioctl(soc, FIONBIO, &nbio); 530 531 return (rc); 532 } 533 #endif 534 535 536 /* 537 * vs_eng_scanstamp_current 538 * 539 * Check if scanstamp matches that of ANY engine with no errors. 540 * We cannot include engines with errors as they may have been 541 * inaccessible for a long time and thus we may have an old 542 * scanstamp value for them. 543 * If a match is found the scanstamp is considered to be current 544 * 545 * returns: 1 if current, 0 otherwise 546 */ 547 int 548 vs_eng_scanstamp_current(vs_scanstamp_t scanstamp) 549 { 550 int i; 551 552 /* if scan stamp is null, not current */ 553 if (scanstamp[0] == '\0') 554 return (0); 555 556 /* if scanstamp matches that of any engine with no errors */ 557 (void) pthread_mutex_lock(&vs_eng_mutex); 558 for (i = 0; i < VS_SE_MAX; i++) { 559 if ((vs_engines[i].vse_error == 0) && 560 (vs_icap_compare_scanstamp(i, scanstamp) == 0)) 561 break; 562 } 563 (void) pthread_mutex_unlock(&vs_eng_mutex); 564 565 return ((i < VS_SE_MAX) ? 1 : 0); 566 } 567 568 569 /* 570 * vs_eng_compare 571 * compare host and port with that stored for engine idx 572 * 573 * Returns: 0 - if equal 574 */ 575 static int 576 vs_eng_compare(int idx, char *host, int port) 577 { 578 if (vs_engines[idx].vse_cfg.vep_port != port) 579 return (-1); 580 581 if (strcmp(vs_engines[idx].vse_cfg.vep_host, host) != 0) 582 return (-1); 583 584 return (0); 585 } 586