/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* vs_eng.c manages the vs_engines array of scan engine.
* Access to the array and other private data is protected by vs_eng_mutex.
* A caller can wait for an available engine connection on vs_eng_cv
*
*/
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <errno.h>
#include <poll.h>
#include <pthread.h>
#include <time.h>
#include <signal.h>
#include <thread.h>
#include "vs_incl.h"
/* max connections per scan engine */
/*
* vs_eng_state_t - connection state
*
* Each configured scan engine supports up to vse_cfg.vep_maxconn
* connections. These connections are represented by a vs_connection_t
* which defines the connection state, associated socket descriptor
* and how long the connection has been available. A connection
* that has been available but unused for vs_inactivity_timeout
* seconds will be closed by the housekeeper thread.
*
* When a scan engine is reconfigured to have less connections
* (or is disabled) any of he superflous connections which are in
* AVAILABLE state are closed (DISCONNECTED). Others are set to
* CLOSE_PENDING to be closed (DISCONNECTED) when the engine is
* released (when the current request completes).
*
* +---------------------+
* |---------->| VS_ENG_DISCONNECTED |<-----------------|
* | +---------------------+ |
* | | |
* | | eng_get |
* | v | release/
* | shutdown +---------------------+ reconfig | shutdown
* |<----------| VS_ENG_RESERVED | -----------| |
* | +---------------------+ | |
* | | v |
* | | +----------------------+
* | | connect | VS_ENG_CLOSE_PENDING |
* | | +----------------------+
* | v ^
* | shutdown +---------------------+ |
* |<----------| VS_ENG_INUSE |------------|
* | | ^
* | | release | eng_get
* | reconfig/ | |
* | timeout/ v |
* | shutdown +---------------------+
* |<----------| VS_ENG_AVAILABLE |
* +---------------------+
*
*/
typedef enum {
VS_ENG_DISCONNECTED = 0,
typedef struct vs_connection {
int vsc_sockfd;
typedef struct vs_engine {
} vs_engine_t;
/* local functions */
static int vs_eng_connect(char *, int);
static boolean_t vs_eng_check_errors(void);
static int vs_eng_find_connection(int *, int *, boolean_t);
static int vs_eng_find_next(boolean_t);
static int vs_eng_compare(int, char *, int);
static void vs_eng_config_close(vs_engine_t *, int);
static void *vs_eng_housekeeper(void *);
#ifdef FIONBIO
/* non-blocking connect */
static int nbio_connect(int, const struct sockaddr *, int);
#endif /* FIONBIO */
/*
* vs_eng_init
*/
void
{
(void) pthread_mutex_lock(&vs_eng_mutex);
vs_eng_total_maxcon = 0;
vs_eng_total_inuse = 0;
vs_eng_count = 0;
vs_eng_next = 0;
(void) pthread_mutex_unlock(&vs_eng_mutex);
}
/*
* vs_eng_config
*
* Configure scan engine connections.
*
* If a scan engine has been reconfigured (different host or port)
* the scan engine's error count is reset.
*
* or less connections are configured now, connections need
* to be closed or placed in CLOSE_PENDING state (vs_eng_config_close)
*
* vs_icap_config is invoked to reset engine-specific data stored
* in vs_icap.
*
*/
void
{
int i;
(void) pthread_mutex_lock(&vs_eng_mutex);
vs_eng_count = 0;
vs_eng_total_maxcon = 0;
for (i = 0; i < VS_SE_MAX; i++) {
eng = &vs_engines[i];
vs_eng_config_close(eng, 0);
}
if (cfg->vep_enable) {
vs_eng_count++;
} else {
vs_eng_config_close(eng, 0);
}
}
if ((vs_eng_total_maxcon <= 0) || (vs_eng_count == 0))
(void) pthread_mutex_unlock(&vs_eng_mutex);
}
/*
* vs_eng_config_close
*
* or less connections are configured now, connections need
* to be closed or placed in CLOSE_PENDING state
*/
static void
{
int i;
case VS_ENG_RESERVED:
case VS_ENG_INUSE:
break;
case VS_ENG_AVAILABLE:
break;
case VS_ENG_CLOSE_PENDING:
case VS_ENG_DISCONNECTED:
break;
}
}
}
/*
* vs_eng_fini
*/
void
{
(void) pthread_cond_destroy(&vs_eng_cv);
}
/*
* vs_eng_housekeeper
*
* Wakeup every (vs_inactivity_timeout / 2) seconds and close
* any connections that are in AVAILABLE state but have not
* been used for vs_inactivity_timeout seconds.
*/
/* ARGSUSED */
static void *
{
long expire;
int i, j;
for (;;) {
if (vscand_get_state() == VS_STATE_SHUTDOWN)
break;
(void) pthread_mutex_lock(&vs_eng_mutex);
for (i = 0; i < VS_SE_MAX; i++) {
eng = &(vs_engines[i]);
}
}
}
(void) pthread_mutex_unlock(&vs_eng_mutex);
}
return (NULL);
}
/*
* vs_eng_set_error
*
* If the engine identified in conn (host, port) matches the
* engine in vs_engines set or clear the error state of the
* engine and update the error statistics.
*
* If error == 0, clear the error state(B_FALSE), else set
* the error state (B_TRUE) and increment engine error stats
*/
void
{
(void) pthread_mutex_lock(&vs_eng_mutex);
if (error != 0) {
}
(void) pthread_mutex_unlock(&vs_eng_mutex);
}
/*
* vs_eng_get
* Get next available scan engine connection.
* If retry == B_TRUE look for a scan engine with no errors.
*
* Returns: 0 - success
* -1 - error
*/
int
{
(void) pthread_mutex_lock(&vs_eng_mutex);
/*
* If no engines connections configured OR
* retry and only one engine configured, give up
*/
if ((vs_eng_total_maxcon <= 0) ||
(void) pthread_mutex_unlock(&vs_eng_mutex);
return (-1);
}
while ((vscand_get_state() != VS_STATE_SHUTDOWN) &&
/* If retry and all configured engines have errors, give up */
(void) pthread_mutex_unlock(&vs_eng_mutex);
return (-1);
}
/* wait for a connection to become available */
&tswait) < 0) {
"- timeout waiting for available engine");
(void) pthread_mutex_unlock(&vs_eng_mutex);
return (-1);
}
}
if (vscand_get_state() == VS_STATE_SHUTDOWN) {
(void) pthread_mutex_unlock(&vs_eng_mutex);
return (-1);
}
/* update in use counts */
/* update round-robin index */
if (!retry)
/* populate vs_eng_ctx_t */
(void) pthread_mutex_unlock(&vs_eng_mutex);
return (0);
}
/* state == VS_ENG_RESERVED, need to connect */
(void) pthread_mutex_unlock(&vs_eng_mutex);
/* retry a failed connection once */
if (sockfd == -1) {
(void) sleep(1);
}
if (sockfd == -1) {
return (-1);
}
(void) pthread_mutex_lock(&vs_eng_mutex);
case VS_ENG_DISCONNECTED:
/* SHUTDOWN occured */
(void) pthread_mutex_unlock(&vs_eng_mutex);
return (-1);
case VS_ENG_RESERVED:
break;
case VS_ENG_CLOSE_PENDING:
/* reconfigure occured. Connection will be closed after use */
break;
case VS_ENG_INUSE:
case VS_ENG_AVAILABLE:
default:
ASSERT(0);
break;
}
(void) pthread_mutex_unlock(&vs_eng_mutex);
return (0);
}
/*
* vs_eng_check_errors
*
* Check if all engines with maxconn > 0 are in error state
*
* Returns: B_TRUE - all (valid) engines are in error state
* B_FALSE - otherwise
*/
static boolean_t
{
int i;
for (i = 0; i < VS_SE_MAX; i++) {
return (B_FALSE);
}
return (B_TRUE);
}
/*
* vs_eng_find_connection
*
* Identify the next engine to be used (vs_eng_find_next()).
* Select the engine's first connection in AVAILABLE state.
* If no connection is in AVAILABLE state, select the first
* that is in DISCONNECTED state.
*
* Returns: 0 success
* -1 no engine connections available (eng_idx & cxn_idx undefined)
*/
static int
{
int i, idx;
/* identify engine */
return (-1);
/* identify connection */
idx = -1;
*cxn_idx = i;
return (0);
}
if ((idx == -1) &&
idx = i;
}
}
if (idx == -1)
return (-1);
return (0);
}
/*
* vs_eng_find_next
*
* Returns: -1 no engine connections available
* idx of engine to use
*/
static int
{
int i;
for (i = vs_eng_next; i < VS_SE_MAX; i++) {
if (vs_engines[i].vse_inuse <
return (i);
}
}
for (i = 0; i < vs_eng_next; i++) {
if (vs_engines[i].vse_inuse <
return (i);
}
}
return (-1);
}
/*
* vs_eng_release
*/
void
{
(void) pthread_mutex_lock(&vs_eng_mutex);
case VS_ENG_DISCONNECTED:
break;
case VS_ENG_RESERVED:
break;
case VS_ENG_INUSE:
if (vs_reuse_connection) {
break;
}
/* LINTED E_CASE_FALL_THROUGH - close connection */
case VS_ENG_CLOSE_PENDING:
break;
case VS_ENG_AVAILABLE:
default:
ASSERT(0);
break;
}
/* decrement in use counts */
/* wake up next thread waiting for a connection */
(void) pthread_cond_signal(&vs_eng_cv);
(void) pthread_mutex_unlock(&vs_eng_mutex);
}
/*
* vs_eng_close_connections
*
* Set vs_eng_total_maxcon to 0 to ensure no new engine sessions
* can be initiated.
* Close all open connections to abort in-progress scans.
* Set connection state to DISCONNECTED.
*/
void
vs_eng_close_connections(void)
{
int i, j;
(void) pthread_mutex_lock(&vs_eng_mutex);
vs_eng_total_maxcon = 0;
for (i = 0; i < VS_SE_MAX; i++) {
for (j = 0; j < VS_CXN_MAX; j++) {
case VS_ENG_INUSE:
case VS_ENG_AVAILABLE:
case VS_ENG_CLOSE_PENDING:
break;
case VS_ENG_DISCONNECTED:
case VS_ENG_RESERVED:
default:
break;
}
}
}
(void) pthread_mutex_unlock(&vs_eng_mutex);
}
/*
* vs_eng_connect
* open socket connection to remote scan engine
*
* Returns: sockfd or -1 (error)
*/
static int
{
return (-1);
return (-1);
}
#ifdef FIONBIO /* Use non-blocking mode for connect. */
sizeof (struct sockaddr));
#else
sizeof (struct sockaddr));
#endif
opt_nodelay = 1;
opt_keepalive = 1;
opt_reuseaddr = 1;
if ((rc < 0) ||
&opt_nodelay, sizeof (opt_nodelay)) < 0) ||
&opt_keepalive, sizeof (opt_keepalive)) < 0) ||
&opt_reuseaddr, sizeof (opt_reuseaddr)) < 0)) {
return (-1);
}
return (sockfd);
}
/*
* nbio_connect
*
* Attempt to do a non-blocking connect call.
* Wait for a maximum of "vs_connect_timeout" millisec, then check for
* socket error to determine if connect successful or not.
*/
#ifdef FIONBIO
static int
{
nbio = 1;
errno = 0;
if (rc == 0)
rc = -1;
} else {
rc = -1;
} else {
rc = -1;
if (error != 0)
}
}
}
}
nbio = 0;
return (rc);
}
#endif
/*
* vs_eng_scanstamp_current
*
* Check if scanstamp matches that of ANY engine with no errors.
* We cannot include engines with errors as they may have been
* inaccessible for a long time and thus we may have an old
* scanstamp value for them.
* If a match is found the scanstamp is considered to be current
*
* returns: 1 if current, 0 otherwise
*/
int
{
int i;
/* if scan stamp is null, not current */
if (scanstamp[0] == '\0')
return (0);
/* if scanstamp matches that of any enabled engine with no errors */
(void) pthread_mutex_lock(&vs_eng_mutex);
for (i = 0; i < VS_SE_MAX; i++) {
(vs_icap_compare_scanstamp(i, scanstamp) == 0))
break;
}
(void) pthread_mutex_unlock(&vs_eng_mutex);
return ((i < VS_SE_MAX) ? 1 : 0);
}
/*
* vs_eng_compare
* compare host and port with that stored for engine idx
*
* Returns: 0 - if equal
*/
static int
{
return (-1);
return (-1);
return (0);
}