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