xref: /illumos-gate/usr/src/cmd/vscan/vscand/vs_eng.c (revision 29a5d005)
1911106dfSjm /*
2911106dfSjm  * CDDL HEADER START
3911106dfSjm  *
4911106dfSjm  * The contents of this file are subject to the terms of the
5911106dfSjm  * Common Development and Distribution License (the "License").
6911106dfSjm  * You may not use this file except in compliance with the License.
7911106dfSjm  *
8911106dfSjm  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9911106dfSjm  * or http://www.opensolaris.org/os/licensing.
10911106dfSjm  * See the License for the specific language governing permissions
11911106dfSjm  * and limitations under the License.
12911106dfSjm  *
13911106dfSjm  * When distributing Covered Code, include this CDDL HEADER in each
14911106dfSjm  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15911106dfSjm  * If applicable, add the following below this CDDL HEADER, with the
16911106dfSjm  * fields enclosed by brackets "[]" replaced with your own identifying
17911106dfSjm  * information: Portions Copyright [yyyy] [name of copyright owner]
18911106dfSjm  *
19911106dfSjm  * CDDL HEADER END
20911106dfSjm  */
21911106dfSjm /*
2253c11029Sjm  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23911106dfSjm  * Use is subject to license terms.
2448bbca81SDaniel Hoffman  * Copyright (c) 2016 by Delphix. All rights reserved.
25911106dfSjm  */
26911106dfSjm 
27911106dfSjm /*
28911106dfSjm  * vs_eng.c manages the vs_engines array of scan engine.
29911106dfSjm  * Access to the array and other private data is protected by vs_eng_mutex.
30911106dfSjm  * A caller can wait for an available engine connection on vs_eng_cv
31911106dfSjm  *
32911106dfSjm  */
33911106dfSjm 
34911106dfSjm #include <sys/types.h>
35911106dfSjm #include <sys/synch.h>
36911106dfSjm #include <sys/socket.h>
37911106dfSjm #include <sys/filio.h>
38911106dfSjm #include <sys/ioctl.h>
39911106dfSjm #include <sys/debug.h>
40bfc848c6Sjm #include <sys/time.h>
41911106dfSjm #include <netinet/in.h>
42911106dfSjm #include <netinet/tcp.h>
43911106dfSjm #include <arpa/inet.h>
44911106dfSjm #include <unistd.h>
45911106dfSjm #include <stdlib.h>
46911106dfSjm #include <string.h>
47911106dfSjm #include <syslog.h>
48911106dfSjm #include <errno.h>
49911106dfSjm #include <poll.h>
50911106dfSjm #include <pthread.h>
51911106dfSjm #include <time.h>
52911106dfSjm 
53bfc848c6Sjm #include <signal.h>
54bfc848c6Sjm #include <thread.h>
55bfc848c6Sjm 
56911106dfSjm #include "vs_incl.h"
57911106dfSjm 
58bfc848c6Sjm /* max connections per scan engine */
59bfc848c6Sjm #define	VS_CXN_MAX	VS_VAL_SE_MAXCONN_MAX
60bfc848c6Sjm 
61bfc848c6Sjm /*
62bfc848c6Sjm  * vs_eng_state_t - connection state
63bfc848c6Sjm  *
64bfc848c6Sjm  * Each configured scan engine supports up to vse_cfg.vep_maxconn
65bfc848c6Sjm  * connections. These connections are represented by a vs_connection_t
66bfc848c6Sjm  * which defines the connection state, associated socket descriptor
67bfc848c6Sjm  * and how long the connection has been available. A connection
68bfc848c6Sjm  * that has been available but unused for vs_inactivity_timeout
69bfc848c6Sjm  * seconds will be closed by the housekeeper thread.
70bfc848c6Sjm  *
71bfc848c6Sjm  * When a scan engine is reconfigured to have less connections
7248bbca81SDaniel Hoffman  * (or is disabled) any of the superflous connections which are in
73bfc848c6Sjm  * AVAILABLE state are closed (DISCONNECTED). Others are set to
74bfc848c6Sjm  * CLOSE_PENDING to be closed (DISCONNECTED) when the engine is
75bfc848c6Sjm  * released (when the current request completes).
76bfc848c6Sjm  *
77bfc848c6Sjm  *              +---------------------+
78bfc848c6Sjm  *  |---------->| VS_ENG_DISCONNECTED |<-----------------|
79bfc848c6Sjm  *  |           +---------------------+                  |
80bfc848c6Sjm  *  |              |                                     |
81bfc848c6Sjm  *  |              | eng_get                             |
82bfc848c6Sjm  *  |              v                                     | release/
83bfc848c6Sjm  *  | shutdown  +---------------------+   reconfig       | shutdown
84bfc848c6Sjm  *  |<----------| VS_ENG_RESERVED     | -----------|     |
85bfc848c6Sjm  *  |           +---------------------+            |     |
86bfc848c6Sjm  *  |              |                               v     |
87bfc848c6Sjm  *  |              |                       +----------------------+
88bfc848c6Sjm  *  |              | connect               | VS_ENG_CLOSE_PENDING |
89bfc848c6Sjm  *  |              |                       +----------------------+
90bfc848c6Sjm  *  |              v                               ^
91bfc848c6Sjm  *  | shutdown  +---------------------+            |
92bfc848c6Sjm  *  |<----------| VS_ENG_INUSE        |------------|
93bfc848c6Sjm  *  |           +---------------------+  reconfig/error
94bfc848c6Sjm  *  |              |           ^
95bfc848c6Sjm  *  |              | release   | eng_get
96bfc848c6Sjm  *  | reconfig/    |           |
97bfc848c6Sjm  *  | timeout/     v           |
98bfc848c6Sjm  *  | shutdown  +---------------------+
99bfc848c6Sjm  *  |<----------| VS_ENG_AVAILABLE    |
100bfc848c6Sjm  *              +---------------------+
101bfc848c6Sjm  *
102bfc848c6Sjm  */
103bfc848c6Sjm 
104bfc848c6Sjm typedef enum {
105bfc848c6Sjm 	VS_ENG_DISCONNECTED = 0,
106bfc848c6Sjm 	VS_ENG_RESERVED,
107bfc848c6Sjm 	VS_ENG_INUSE,
108bfc848c6Sjm 	VS_ENG_AVAILABLE,
109bfc848c6Sjm 	VS_ENG_CLOSE_PENDING
110bfc848c6Sjm } vs_eng_state_t;
111bfc848c6Sjm 
112bfc848c6Sjm typedef struct vs_connection {
113bfc848c6Sjm 	vs_eng_state_t vsc_state;
114bfc848c6Sjm 	int vsc_sockfd;
115bfc848c6Sjm 	struct timeval vsc_avail_time;
116bfc848c6Sjm } vs_connection_t;
117911106dfSjm 
118911106dfSjm typedef struct vs_engine {
119bfc848c6Sjm 	vs_props_se_t vse_cfg;	/* host, port, maxconn */
120bfc848c6Sjm 	int vse_inuse;		/* # connections in use */
121bfc848c6Sjm 	boolean_t vse_error;
122bfc848c6Sjm 	vs_connection_t vse_cxns[VS_CXN_MAX];
123911106dfSjm } vs_engine_t;
124911106dfSjm 
125911106dfSjm static vs_engine_t vs_engines[VS_SE_MAX];
126911106dfSjm 
127911106dfSjm static int vs_eng_next;		/* round-robin "finger" */
128911106dfSjm static int vs_eng_count;	/* how many configured engines */
129911106dfSjm static int vs_eng_total_maxcon;	/* total configured connections */
130911106dfSjm static int vs_eng_total_inuse;	/* total connections in use */
131911106dfSjm static int vs_eng_wait_count;	/* # threads waiting for connection */
132911106dfSjm 
133911106dfSjm static pthread_mutex_t vs_eng_mutex = PTHREAD_MUTEX_INITIALIZER;
134911106dfSjm static pthread_cond_t vs_eng_cv;
135bfc848c6Sjm int vs_inactivity_timeout = 60; /* seconds */
136bfc848c6Sjm int vs_reuse_connection = 1;
137911106dfSjm 
138bfc848c6Sjm time_t vs_eng_wait = VS_ENG_WAIT_DFLT;
139911106dfSjm 
140911106dfSjm /* local functions */
141bfc848c6Sjm static int vs_eng_connect(char *, int);
142bfc848c6Sjm static boolean_t vs_eng_check_errors(void);
143bfc848c6Sjm static int vs_eng_find_connection(int *, int *, boolean_t);
144bfc848c6Sjm static int vs_eng_find_next(boolean_t);
145911106dfSjm static int vs_eng_compare(int, char *, int);
146bfc848c6Sjm static void vs_eng_config_close(vs_engine_t *, int);
147bfc848c6Sjm static void *vs_eng_housekeeper(void *);
148911106dfSjm 
149911106dfSjm 
150911106dfSjm #ifdef FIONBIO
151911106dfSjm /* non-blocking connect */
152bfc848c6Sjm static int nbio_connect(int, const struct sockaddr *, int);
153911106dfSjm int vs_connect_timeout = 5000; /* milliseconds */
154911106dfSjm #endif /* FIONBIO */
155911106dfSjm 
156911106dfSjm 
157911106dfSjm /*
158911106dfSjm  * vs_eng_init
159911106dfSjm  */
160911106dfSjm void
vs_eng_init()161911106dfSjm vs_eng_init()
162911106dfSjm {
163bfc848c6Sjm 	pthread_t tid;
164bfc848c6Sjm 
165911106dfSjm 	(void) pthread_cond_init(&vs_eng_cv, NULL);
166911106dfSjm 	(void) pthread_mutex_lock(&vs_eng_mutex);
167911106dfSjm 
168bfc848c6Sjm 	(void) memset(vs_engines, 0, sizeof (vs_engine_t) * VS_SE_MAX);
169bfc848c6Sjm 
170911106dfSjm 	vs_eng_total_maxcon = 0;
171911106dfSjm 	vs_eng_total_inuse = 0;
172911106dfSjm 	vs_eng_count = 0;
173911106dfSjm 	vs_eng_next = 0;
174911106dfSjm 
175911106dfSjm 	(void) pthread_mutex_unlock(&vs_eng_mutex);
176bfc848c6Sjm 
177bfc848c6Sjm 	(void) pthread_create(&tid, NULL, vs_eng_housekeeper, NULL);
178911106dfSjm }
179911106dfSjm 
180911106dfSjm 
181911106dfSjm /*
182911106dfSjm  * vs_eng_config
183911106dfSjm  *
184911106dfSjm  * Configure scan engine connections.
185911106dfSjm  *
186911106dfSjm  * If a scan engine has been reconfigured (different host or port)
18753c11029Sjm  * the scan engine's error count is reset.
18853c11029Sjm  *
189bfc848c6Sjm  * If the host/port has changed, the engine has been disabled
190bfc848c6Sjm  * or less connections are configured now, connections need
191bfc848c6Sjm  * to be closed or placed in CLOSE_PENDING state (vs_eng_config_close)
192bfc848c6Sjm  *
193911106dfSjm  * vs_icap_config is invoked to reset engine-specific data stored
19453c11029Sjm  * in vs_icap.
195911106dfSjm  *
196911106dfSjm  */
197911106dfSjm void
vs_eng_config(vs_props_all_t * config)198911106dfSjm vs_eng_config(vs_props_all_t *config)
199911106dfSjm {
200911106dfSjm 	int i;
201911106dfSjm 	vs_props_se_t *cfg;
20253c11029Sjm 	vs_engine_t *eng;
203911106dfSjm 
204911106dfSjm 	(void) pthread_mutex_lock(&vs_eng_mutex);
205911106dfSjm 
206911106dfSjm 	vs_eng_count = 0;
207911106dfSjm 	vs_eng_total_maxcon = 0;
208911106dfSjm 
209911106dfSjm 	for (i = 0; i < VS_SE_MAX; i++) {
210911106dfSjm 		cfg = &config->va_se[i];
21153c11029Sjm 		eng = &vs_engines[i];
212911106dfSjm 
213bfc848c6Sjm 		if (vs_eng_compare(i, cfg->vep_host, cfg->vep_port) != 0) {
214bfc848c6Sjm 			vs_eng_config_close(eng, 0);
215bfc848c6Sjm 			eng->vse_error = B_FALSE;
216bfc848c6Sjm 		}
217911106dfSjm 
218911106dfSjm 		if (cfg->vep_enable) {
219bfc848c6Sjm 			if (cfg->vep_maxconn < eng->vse_cfg.vep_maxconn)
220bfc848c6Sjm 				vs_eng_config_close(eng, cfg->vep_maxconn);
221bfc848c6Sjm 
22253c11029Sjm 			eng->vse_cfg = *cfg;
223911106dfSjm 			vs_eng_total_maxcon += cfg->vep_maxconn;
224911106dfSjm 			vs_eng_count++;
225911106dfSjm 		} else {
226bfc848c6Sjm 			vs_eng_config_close(eng, 0);
22753c11029Sjm 			(void) memset(&eng->vse_cfg, 0, sizeof (vs_props_se_t));
228911106dfSjm 		}
229911106dfSjm 
23053c11029Sjm 		vs_icap_config(i, eng->vse_cfg.vep_host, eng->vse_cfg.vep_port);
231911106dfSjm 	}
232911106dfSjm 
233911106dfSjm 	if ((vs_eng_total_maxcon <= 0) || (vs_eng_count == 0))
234c8dbf746Sjm 		syslog(LOG_NOTICE, "Scan Engine - no engines configured");
235911106dfSjm 
236911106dfSjm 	(void) pthread_mutex_unlock(&vs_eng_mutex);
237911106dfSjm }
238911106dfSjm 
239911106dfSjm 
240911106dfSjm /*
241bfc848c6Sjm  * vs_eng_config_close
242911106dfSjm  *
243bfc848c6Sjm  *	If the host/port has changed, the engine has been disabled
244bfc848c6Sjm  *	or less connections are configured now, connections need
245bfc848c6Sjm  *	to be closed or placed in CLOSE_PENDING state
246bfc848c6Sjm  */
247bfc848c6Sjm static void
vs_eng_config_close(vs_engine_t * eng,int start_idx)248bfc848c6Sjm vs_eng_config_close(vs_engine_t *eng, int start_idx)
249bfc848c6Sjm {
250bfc848c6Sjm 	int i;
251bfc848c6Sjm 	vs_connection_t *cxn;
252bfc848c6Sjm 
253bfc848c6Sjm 	for (i = start_idx; i < eng->vse_cfg.vep_maxconn; i++) {
254bfc848c6Sjm 		cxn = &(eng->vse_cxns[i]);
255bfc848c6Sjm 
256bfc848c6Sjm 		switch (cxn->vsc_state) {
257bfc848c6Sjm 		case VS_ENG_RESERVED:
258bfc848c6Sjm 		case VS_ENG_INUSE:
259bfc848c6Sjm 			cxn->vsc_state = VS_ENG_CLOSE_PENDING;
260bfc848c6Sjm 			break;
261bfc848c6Sjm 		case VS_ENG_AVAILABLE:
262bfc848c6Sjm 			(void) close(cxn->vsc_sockfd);
263bfc848c6Sjm 			cxn->vsc_sockfd = -1;
264bfc848c6Sjm 			cxn->vsc_state = VS_ENG_DISCONNECTED;
265bfc848c6Sjm 			break;
266bfc848c6Sjm 		case VS_ENG_CLOSE_PENDING:
267bfc848c6Sjm 		case VS_ENG_DISCONNECTED:
268bfc848c6Sjm 			break;
269bfc848c6Sjm 		}
270bfc848c6Sjm 	}
271bfc848c6Sjm }
272bfc848c6Sjm 
273bfc848c6Sjm 
274bfc848c6Sjm /*
275bfc848c6Sjm  * vs_eng_fini
276911106dfSjm  */
277911106dfSjm void
vs_eng_fini()278911106dfSjm vs_eng_fini()
279911106dfSjm {
280bfc848c6Sjm 	(void) pthread_cond_destroy(&vs_eng_cv);
281bfc848c6Sjm }
282911106dfSjm 
283911106dfSjm 
284bfc848c6Sjm /*
285bfc848c6Sjm  * vs_eng_housekeeper
286bfc848c6Sjm  *
287bfc848c6Sjm  * Wakeup every (vs_inactivity_timeout / 2) seconds and close
288bfc848c6Sjm  * any connections that are in AVAILABLE state but have not
289bfc848c6Sjm  * been used for vs_inactivity_timeout seconds.
290bfc848c6Sjm  */
291bfc848c6Sjm /* ARGSUSED */
292bfc848c6Sjm static void *
vs_eng_housekeeper(void * arg)293bfc848c6Sjm vs_eng_housekeeper(void *arg)
294bfc848c6Sjm {
295bfc848c6Sjm 	struct timeval now;
296bfc848c6Sjm 	long expire;
297bfc848c6Sjm 	int i, j;
298bfc848c6Sjm 	vs_engine_t *eng;
299bfc848c6Sjm 	vs_connection_t *cxn;
300911106dfSjm 
301bfc848c6Sjm 	for (;;) {
302bfc848c6Sjm 		(void) sleep(vs_inactivity_timeout / 2);
303911106dfSjm 
304bfc848c6Sjm 		if (vscand_get_state() == VS_STATE_SHUTDOWN)
305bfc848c6Sjm 			break;
306bfc848c6Sjm 
307bfc848c6Sjm 		(void) gettimeofday(&now, NULL);
308bfc848c6Sjm 		expire = now.tv_sec - vs_inactivity_timeout;
309bfc848c6Sjm 
310bfc848c6Sjm 		(void) pthread_mutex_lock(&vs_eng_mutex);
311bfc848c6Sjm 		for (i = 0; i < VS_SE_MAX; i++) {
312bfc848c6Sjm 			eng = &(vs_engines[i]);
313bfc848c6Sjm 			for (j = 0; j < eng->vse_cfg.vep_maxconn; j++) {
314bfc848c6Sjm 				cxn = &(eng->vse_cxns[j]);
315bfc848c6Sjm 
316bfc848c6Sjm 				if ((cxn->vsc_state == VS_ENG_AVAILABLE) &&
317bfc848c6Sjm 				    (cxn->vsc_avail_time.tv_sec < expire)) {
318bfc848c6Sjm 					(void) close(cxn->vsc_sockfd);
319bfc848c6Sjm 					cxn->vsc_sockfd = -1;
320bfc848c6Sjm 					cxn->vsc_state = VS_ENG_DISCONNECTED;
321bfc848c6Sjm 				}
322bfc848c6Sjm 			}
323bfc848c6Sjm 		}
324bfc848c6Sjm 		(void) pthread_mutex_unlock(&vs_eng_mutex);
325bfc848c6Sjm 	}
326bfc848c6Sjm 
327bfc848c6Sjm 	return (NULL);
328911106dfSjm }
329911106dfSjm 
330911106dfSjm 
331911106dfSjm /*
332911106dfSjm  * vs_eng_set_error
333911106dfSjm  *
334911106dfSjm  * If the engine identified in conn (host, port) matches the
335911106dfSjm  * engine in vs_engines set or clear the error state of the
336911106dfSjm  * engine and update the error statistics.
337911106dfSjm  *
338bfc848c6Sjm  * If error == 0, clear the error state(B_FALSE), else set
339bfc848c6Sjm  * the error state (B_TRUE) and increment engine error stats
340911106dfSjm  */
341911106dfSjm void
vs_eng_set_error(vs_eng_ctx_t * eng_ctx,int error)342bfc848c6Sjm vs_eng_set_error(vs_eng_ctx_t *eng_ctx, int error)
343911106dfSjm {
344bfc848c6Sjm 	int eidx = eng_ctx->vse_eidx;
345bfc848c6Sjm 	int cidx =  eng_ctx->vse_cidx;
346bfc848c6Sjm 	vs_engine_t *eng;
347911106dfSjm 
348911106dfSjm 	(void) pthread_mutex_lock(&vs_eng_mutex);
349911106dfSjm 
350bfc848c6Sjm 	eng = &(vs_engines[eidx]);
351bfc848c6Sjm 
352bfc848c6Sjm 	if (vs_eng_compare(eidx, eng_ctx->vse_host, eng_ctx->vse_port) == 0)
353bfc848c6Sjm 		eng->vse_error = (error == 0) ? B_FALSE : B_TRUE;
354bfc848c6Sjm 
355bfc848c6Sjm 	if (error != 0) {
356bfc848c6Sjm 		eng->vse_cxns[cidx].vsc_state = VS_ENG_CLOSE_PENDING;
357bfc848c6Sjm 		vs_stats_eng_err(eng_ctx->vse_engid);
358bfc848c6Sjm 	}
359911106dfSjm 
360911106dfSjm 	(void) pthread_mutex_unlock(&vs_eng_mutex);
361911106dfSjm }
362911106dfSjm 
363911106dfSjm 
364911106dfSjm /*
365911106dfSjm  * vs_eng_get
366911106dfSjm  * Get next available scan engine connection.
367bfc848c6Sjm  * If retry == B_TRUE look for a scan engine with no errors.
368911106dfSjm  *
369911106dfSjm  * Returns: 0 - success
370911106dfSjm  *         -1 - error
371911106dfSjm  */
372911106dfSjm int
vs_eng_get(vs_eng_ctx_t * eng_ctx,boolean_t retry)373bfc848c6Sjm vs_eng_get(vs_eng_ctx_t *eng_ctx, boolean_t retry)
374911106dfSjm {
375911106dfSjm 	struct timespec tswait;
376bfc848c6Sjm 	int eidx, cidx, sockfd;
377bfc848c6Sjm 	vs_engine_t *eng;
378bfc848c6Sjm 	vs_connection_t *cxn;
379911106dfSjm 
380911106dfSjm 	(void) pthread_mutex_lock(&vs_eng_mutex);
381911106dfSjm 
382911106dfSjm 	/*
383bfc848c6Sjm 	 * If no engines connections configured OR
384911106dfSjm 	 * retry and only one engine configured, give up
385911106dfSjm 	 */
386bfc848c6Sjm 	if ((vs_eng_total_maxcon <= 0) ||
387bfc848c6Sjm 	    ((retry == B_TRUE) && (vs_eng_count <= 1))) {
388911106dfSjm 		(void) pthread_mutex_unlock(&vs_eng_mutex);
389911106dfSjm 		return (-1);
390911106dfSjm 	}
391911106dfSjm 
392911106dfSjm 	tswait.tv_sec = vs_eng_wait;
393911106dfSjm 	tswait.tv_nsec = 0;
394911106dfSjm 
395911106dfSjm 	while ((vscand_get_state() != VS_STATE_SHUTDOWN) &&
396bfc848c6Sjm 	    (vs_eng_find_connection(&eidx, &cidx, retry) == -1)) {
397911106dfSjm 		/* If retry and all configured engines have errors, give up */
398bfc848c6Sjm 		if (retry && vs_eng_check_errors() == B_TRUE) {
399911106dfSjm 			(void) pthread_mutex_unlock(&vs_eng_mutex);
400911106dfSjm 			return (-1);
401911106dfSjm 		}
402911106dfSjm 
403911106dfSjm 		/* wait for a connection to become available */
404911106dfSjm 		vs_eng_wait_count++;
405911106dfSjm 		if (pthread_cond_reltimedwait_np(&vs_eng_cv, &vs_eng_mutex,
406911106dfSjm 		    &tswait) < 0) {
407c8dbf746Sjm 			syslog(LOG_NOTICE, "Scan Engine "
408911106dfSjm 			    "- timeout waiting for available engine");
409911106dfSjm 			vs_eng_wait_count--;
410911106dfSjm 			(void) pthread_mutex_unlock(&vs_eng_mutex);
411911106dfSjm 			return (-1);
412911106dfSjm 		}
413911106dfSjm 		vs_eng_wait_count--;
414911106dfSjm 	}
415911106dfSjm 
416911106dfSjm 	if (vscand_get_state() == VS_STATE_SHUTDOWN) {
417911106dfSjm 		(void) pthread_mutex_unlock(&vs_eng_mutex);
418911106dfSjm 		return (-1);
419911106dfSjm 	}
420911106dfSjm 
421bfc848c6Sjm 	eng = &(vs_engines[eidx]);
422bfc848c6Sjm 	cxn = &(eng->vse_cxns[cidx]);
423911106dfSjm 
424911106dfSjm 	/* update in use counts */
425bfc848c6Sjm 	eng->vse_inuse++;
426911106dfSjm 	vs_eng_total_inuse++;
427911106dfSjm 
428911106dfSjm 	/* update round-robin index */
429911106dfSjm 	if (!retry)
430bfc848c6Sjm 		vs_eng_next = (eidx == VS_SE_MAX) ? 0 : eidx + 1;
431bfc848c6Sjm 
432bfc848c6Sjm 	/* populate vs_eng_ctx_t */
433bfc848c6Sjm 	eng_ctx->vse_eidx = eidx;
434bfc848c6Sjm 	eng_ctx->vse_cidx = cidx;
435bfc848c6Sjm 	(void) strlcpy(eng_ctx->vse_engid, eng->vse_cfg.vep_engid,
436bfc848c6Sjm 	    sizeof (eng_ctx->vse_engid));
437bfc848c6Sjm 	(void) strlcpy(eng_ctx->vse_host, eng->vse_cfg.vep_host,
438bfc848c6Sjm 	    sizeof (eng_ctx->vse_host));
439bfc848c6Sjm 	eng_ctx->vse_port = eng->vse_cfg.vep_port;
440bfc848c6Sjm 	eng_ctx->vse_sockfd = cxn->vsc_sockfd;
441bfc848c6Sjm 
442bfc848c6Sjm 	if (cxn->vsc_state == VS_ENG_INUSE) {
443bfc848c6Sjm 		(void) pthread_mutex_unlock(&vs_eng_mutex);
444bfc848c6Sjm 		return (0);
445bfc848c6Sjm 	}
446bfc848c6Sjm 
447bfc848c6Sjm 	/* state == VS_ENG_RESERVED, need to connect */
448911106dfSjm 
449911106dfSjm 	(void) pthread_mutex_unlock(&vs_eng_mutex);
450911106dfSjm 
451bfc848c6Sjm 	sockfd = vs_eng_connect(eng_ctx->vse_host, eng_ctx->vse_port);
452bfc848c6Sjm 
453bfc848c6Sjm 	/* retry a failed connection once */
454bfc848c6Sjm 	if (sockfd == -1) {
455bfc848c6Sjm 		(void) sleep(1);
456bfc848c6Sjm 		sockfd = vs_eng_connect(eng_ctx->vse_host, eng_ctx->vse_port);
457bfc848c6Sjm 	}
458bfc848c6Sjm 
459bfc848c6Sjm 	if (sockfd == -1) {
460c8dbf746Sjm 		syslog(LOG_NOTICE, "Scan Engine - connection error (%s:%d) %s",
461bfc848c6Sjm 		    eng_ctx->vse_host, eng_ctx->vse_port,
462bfc848c6Sjm 		    errno ? strerror(errno) : "");
463bfc848c6Sjm 		vs_eng_set_error(eng_ctx, 1);
464bfc848c6Sjm 		vs_eng_release(eng_ctx);
465bfc848c6Sjm 		return (-1);
466bfc848c6Sjm 	}
467bfc848c6Sjm 
468bfc848c6Sjm 	(void) pthread_mutex_lock(&vs_eng_mutex);
469bfc848c6Sjm 	switch (cxn->vsc_state) {
470bfc848c6Sjm 	case VS_ENG_DISCONNECTED:
471bfc848c6Sjm 		/* SHUTDOWN occured */
472bfc848c6Sjm 		(void) pthread_mutex_unlock(&vs_eng_mutex);
473bfc848c6Sjm 		vs_eng_release(eng_ctx);
474bfc848c6Sjm 		return (-1);
475bfc848c6Sjm 	case VS_ENG_RESERVED:
476bfc848c6Sjm 		cxn->vsc_state = VS_ENG_INUSE;
477bfc848c6Sjm 		break;
478bfc848c6Sjm 	case VS_ENG_CLOSE_PENDING:
479bfc848c6Sjm 		/* reconfigure occured. Connection will be closed after use */
480bfc848c6Sjm 		break;
481bfc848c6Sjm 	case VS_ENG_INUSE:
482bfc848c6Sjm 	case VS_ENG_AVAILABLE:
483bfc848c6Sjm 	default:
484bfc848c6Sjm 		ASSERT(0);
485bfc848c6Sjm 		break;
486bfc848c6Sjm 	}
487bfc848c6Sjm 
488bfc848c6Sjm 	cxn->vsc_sockfd = sockfd;
489bfc848c6Sjm 	eng_ctx->vse_sockfd = sockfd;
490bfc848c6Sjm 
491bfc848c6Sjm 	(void) pthread_mutex_unlock(&vs_eng_mutex);
492911106dfSjm 	return (0);
493911106dfSjm }
494911106dfSjm 
495911106dfSjm 
496911106dfSjm /*
497911106dfSjm  * vs_eng_check_errors
498911106dfSjm  *
499bfc848c6Sjm  * Check if all engines with maxconn > 0 are in error state
500911106dfSjm  *
501bfc848c6Sjm  * Returns: B_TRUE  - all (valid) engines are in error state
502bfc848c6Sjm  *          B_FALSE - otherwise
503911106dfSjm  */
504bfc848c6Sjm static boolean_t
vs_eng_check_errors()505911106dfSjm vs_eng_check_errors()
506911106dfSjm {
507911106dfSjm 	int i;
508911106dfSjm 
509911106dfSjm 	for (i = 0; i < VS_SE_MAX; i++) {
510911106dfSjm 		if (vs_engines[i].vse_cfg.vep_maxconn > 0 &&
511bfc848c6Sjm 		    (vs_engines[i].vse_error == B_FALSE))
512bfc848c6Sjm 			return (B_FALSE);
513bfc848c6Sjm 	}
514bfc848c6Sjm 
515bfc848c6Sjm 	return (B_TRUE);
516bfc848c6Sjm }
517bfc848c6Sjm 
518bfc848c6Sjm 
519bfc848c6Sjm /*
520bfc848c6Sjm  * vs_eng_find_connection
521bfc848c6Sjm  *
522bfc848c6Sjm  * Identify the next engine to be used (vs_eng_find_next()).
523bfc848c6Sjm  * Select the engine's first connection in AVAILABLE state.
524bfc848c6Sjm  * If no connection is in AVAILABLE state, select the first
525bfc848c6Sjm  * that is in DISCONNECTED state.
526bfc848c6Sjm  *
527bfc848c6Sjm  * Returns: 0 success
528bfc848c6Sjm  *         -1 no engine connections available (eng_idx & cxn_idx undefined)
529bfc848c6Sjm  */
530bfc848c6Sjm static int
vs_eng_find_connection(int * eng_idx,int * cxn_idx,boolean_t retry)531bfc848c6Sjm vs_eng_find_connection(int *eng_idx, int *cxn_idx, boolean_t retry)
532bfc848c6Sjm {
533bfc848c6Sjm 	int i, idx;
534bfc848c6Sjm 	vs_engine_t *eng;
535bfc848c6Sjm 	vs_connection_t *cxn;
536bfc848c6Sjm 
537bfc848c6Sjm 	/* identify engine */
538bfc848c6Sjm 	if ((idx = vs_eng_find_next(retry)) == -1)
539bfc848c6Sjm 		return (-1);
540bfc848c6Sjm 
541bfc848c6Sjm 	eng = &(vs_engines[idx]);
542bfc848c6Sjm 	*eng_idx = idx;
543bfc848c6Sjm 
544bfc848c6Sjm 	/* identify connection */
545bfc848c6Sjm 	idx = -1;
546bfc848c6Sjm 	for (i = 0; i < eng->vse_cfg.vep_maxconn; i++) {
547bfc848c6Sjm 		cxn = &(eng->vse_cxns[i]);
548bfc848c6Sjm 		if (cxn->vsc_state == VS_ENG_AVAILABLE) {
549bfc848c6Sjm 			*cxn_idx = i;
550bfc848c6Sjm 			cxn->vsc_state = VS_ENG_INUSE;
551911106dfSjm 			return (0);
552bfc848c6Sjm 		}
553bfc848c6Sjm 
554bfc848c6Sjm 		if ((idx == -1) &&
555bfc848c6Sjm 		    (cxn->vsc_state == VS_ENG_DISCONNECTED)) {
556bfc848c6Sjm 			idx = i;
557bfc848c6Sjm 		}
558911106dfSjm 	}
559911106dfSjm 
560bfc848c6Sjm 	if (idx == -1)
561bfc848c6Sjm 		return (-1);
562bfc848c6Sjm 
563bfc848c6Sjm 	eng->vse_cxns[idx].vsc_state = VS_ENG_RESERVED;
564bfc848c6Sjm 	*cxn_idx = idx;
565bfc848c6Sjm 	return (0);
566911106dfSjm }
567911106dfSjm 
568911106dfSjm 
569911106dfSjm /*
570911106dfSjm  * vs_eng_find_next
571911106dfSjm  *
572911106dfSjm  * Returns: -1 no engine connections available
573bfc848c6Sjm  *          idx of engine to use
574911106dfSjm  */
575911106dfSjm static int
vs_eng_find_next(boolean_t retry)576bfc848c6Sjm vs_eng_find_next(boolean_t retry)
577911106dfSjm {
578911106dfSjm 	int i;
579911106dfSjm 
580911106dfSjm 	for (i = vs_eng_next; i < VS_SE_MAX; i++) {
581bfc848c6Sjm 		if (vs_engines[i].vse_inuse <
582911106dfSjm 		    vs_engines[i].vse_cfg.vep_maxconn) {
583bfc848c6Sjm 			if (!retry || (vs_engines[i].vse_error == B_FALSE))
584911106dfSjm 				return (i);
585911106dfSjm 		}
586911106dfSjm 	}
587911106dfSjm 
588911106dfSjm 	for (i = 0; i < vs_eng_next; i++) {
589bfc848c6Sjm 		if (vs_engines[i].vse_inuse <
590911106dfSjm 		    vs_engines[i].vse_cfg.vep_maxconn) {
591bfc848c6Sjm 			if (!retry || (vs_engines[i].vse_error == B_FALSE))
592911106dfSjm 				return (i);
593911106dfSjm 		}
594911106dfSjm 	}
595911106dfSjm 
596911106dfSjm 	return (-1);
597911106dfSjm }
598911106dfSjm 
599911106dfSjm 
600911106dfSjm /*
601911106dfSjm  * vs_eng_release
602911106dfSjm  */
603911106dfSjm void
vs_eng_release(const vs_eng_ctx_t * eng_ctx)604bfc848c6Sjm vs_eng_release(const vs_eng_ctx_t *eng_ctx)
605911106dfSjm {
606bfc848c6Sjm 	int eidx = eng_ctx->vse_eidx;
607bfc848c6Sjm 	int cidx = eng_ctx->vse_cidx;
608bfc848c6Sjm 	vs_connection_t *cxn;
609911106dfSjm 
610911106dfSjm 	(void) pthread_mutex_lock(&vs_eng_mutex);
611bfc848c6Sjm 	cxn = &(vs_engines[eidx].vse_cxns[cidx]);
612bfc848c6Sjm 
613bfc848c6Sjm 	switch (cxn->vsc_state) {
614bfc848c6Sjm 	case VS_ENG_DISCONNECTED:
615bfc848c6Sjm 		break;
616bfc848c6Sjm 	case VS_ENG_RESERVED:
617bfc848c6Sjm 		cxn->vsc_state = VS_ENG_DISCONNECTED;
618bfc848c6Sjm 		break;
619bfc848c6Sjm 	case VS_ENG_INUSE:
620bfc848c6Sjm 		if (vs_reuse_connection) {
621bfc848c6Sjm 			cxn->vsc_state = VS_ENG_AVAILABLE;
622bfc848c6Sjm 			(void) gettimeofday(&cxn->vsc_avail_time, NULL);
623bfc848c6Sjm 			break;
624bfc848c6Sjm 		}
625*29a5d005SToomas Soome 		/* FALLTHROUGH */
626bfc848c6Sjm 	case VS_ENG_CLOSE_PENDING:
627bfc848c6Sjm 		(void) close(cxn->vsc_sockfd);
628bfc848c6Sjm 		cxn->vsc_sockfd = -1;
629bfc848c6Sjm 		cxn->vsc_state = VS_ENG_DISCONNECTED;
630bfc848c6Sjm 		break;
631bfc848c6Sjm 	case VS_ENG_AVAILABLE:
632bfc848c6Sjm 	default:
633bfc848c6Sjm 		ASSERT(0);
634bfc848c6Sjm 		break;
635bfc848c6Sjm 	}
636911106dfSjm 
637911106dfSjm 	/* decrement in use counts */
638bfc848c6Sjm 	vs_engines[eidx].vse_inuse--;
639911106dfSjm 	vs_eng_total_inuse--;
640911106dfSjm 
641911106dfSjm 	/* wake up next thread waiting for a connection */
642911106dfSjm 	(void) pthread_cond_signal(&vs_eng_cv);
643911106dfSjm 
644911106dfSjm 	(void) pthread_mutex_unlock(&vs_eng_mutex);
645911106dfSjm }
646911106dfSjm 
647911106dfSjm 
648911106dfSjm /*
649911106dfSjm  * vs_eng_close_connections
650bfc848c6Sjm  *
651bfc848c6Sjm  * Set vs_eng_total_maxcon to 0 to ensure no new engine sessions
652bfc848c6Sjm  * can be initiated.
653911106dfSjm  * Close all open connections to abort in-progress scans.
654bfc848c6Sjm  * Set connection state to DISCONNECTED.
655911106dfSjm  */
656bfc848c6Sjm void
vs_eng_close_connections(void)657911106dfSjm vs_eng_close_connections(void)
658911106dfSjm {
659bfc848c6Sjm 	int i, j;
660bfc848c6Sjm 	vs_connection_t *cxn;
661bfc848c6Sjm 
662bfc848c6Sjm 	(void) pthread_mutex_lock(&vs_eng_mutex);
663bfc848c6Sjm 	vs_eng_total_maxcon = 0;
664911106dfSjm 
665911106dfSjm 	for (i = 0; i < VS_SE_MAX; i++) {
666bfc848c6Sjm 		for (j = 0; j < VS_CXN_MAX; j++) {
667bfc848c6Sjm 			cxn = &(vs_engines[i].vse_cxns[j]);
668bfc848c6Sjm 
669bfc848c6Sjm 			switch (cxn->vsc_state) {
670bfc848c6Sjm 			case VS_ENG_INUSE:
671bfc848c6Sjm 			case VS_ENG_AVAILABLE:
672bfc848c6Sjm 			case VS_ENG_CLOSE_PENDING:
673bfc848c6Sjm 				(void) close(cxn->vsc_sockfd);
674bfc848c6Sjm 				cxn->vsc_sockfd = -1;
675bfc848c6Sjm 				break;
676bfc848c6Sjm 			case VS_ENG_DISCONNECTED:
677bfc848c6Sjm 			case VS_ENG_RESERVED:
678bfc848c6Sjm 			default:
679bfc848c6Sjm 				break;
680bfc848c6Sjm 
681bfc848c6Sjm 			}
682bfc848c6Sjm 
683bfc848c6Sjm 			cxn->vsc_state = VS_ENG_DISCONNECTED;
684911106dfSjm 		}
685911106dfSjm 	}
686bfc848c6Sjm 	(void) pthread_mutex_unlock(&vs_eng_mutex);
687911106dfSjm }
688911106dfSjm 
689911106dfSjm 
690911106dfSjm /*
691911106dfSjm  * vs_eng_connect
692911106dfSjm  * open socket connection to remote scan engine
693bfc848c6Sjm  *
694bfc848c6Sjm  * Returns: sockfd or -1 (error)
695911106dfSjm  */
696bfc848c6Sjm static int
vs_eng_connect(char * host,int port)697bfc848c6Sjm vs_eng_connect(char *host, int port)
698911106dfSjm {
699bfc848c6Sjm 	int rc, sockfd, opt_nodelay, opt_keepalive, opt_reuseaddr, err_num;
700911106dfSjm 	struct sockaddr_in addr;
701911106dfSjm 	struct hostent *hp;
702911106dfSjm 
703bfc848c6Sjm 	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
704911106dfSjm 		return (-1);
705911106dfSjm 
706bfc848c6Sjm 	hp = getipnodebyname(host, AF_INET, 0, &err_num);
707bfc848c6Sjm 	if (hp == NULL) {
708bfc848c6Sjm 		(void) close(sockfd);
709911106dfSjm 		return (-1);
710bfc848c6Sjm 	}
711911106dfSjm 
712911106dfSjm 	(void) memset(&addr, 0, sizeof (addr));
713911106dfSjm 	(void) memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
714bfc848c6Sjm 	addr.sin_port = htons(port);
715911106dfSjm 	addr.sin_family = hp->h_addrtype;
716911106dfSjm 	freehostent(hp);
717911106dfSjm 
718911106dfSjm #ifdef FIONBIO /* Use non-blocking mode for connect. */
719bfc848c6Sjm 	rc = nbio_connect(sockfd, (struct sockaddr *)&addr,
720911106dfSjm 	    sizeof (struct sockaddr));
721911106dfSjm #else
722bfc848c6Sjm 	rc = connect(sockfd, (struct sockaddr *)&addr,
723911106dfSjm 	    sizeof (struct sockaddr));
724911106dfSjm #endif
725911106dfSjm 
726bfc848c6Sjm 	opt_nodelay = 1;
727bfc848c6Sjm 	opt_keepalive = 1;
728bfc848c6Sjm 	opt_reuseaddr = 1;
729bfc848c6Sjm 
730bfc848c6Sjm 	if ((rc < 0) ||
731bfc848c6Sjm 	    (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY,
732bfc848c6Sjm 	    &opt_nodelay, sizeof (opt_nodelay)) < 0) ||
733bfc848c6Sjm 	    (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE,
734bfc848c6Sjm 	    &opt_keepalive, sizeof (opt_keepalive)) < 0) ||
735bfc848c6Sjm 	    (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
736bfc848c6Sjm 	    &opt_reuseaddr, sizeof (opt_reuseaddr)) < 0)) {
737bfc848c6Sjm 		(void) close(sockfd);
738911106dfSjm 		return (-1);
739911106dfSjm 	}
740911106dfSjm 
741bfc848c6Sjm 	return (sockfd);
742911106dfSjm }
743911106dfSjm 
744911106dfSjm 
745911106dfSjm /*
746911106dfSjm  * nbio_connect
747911106dfSjm  *
748911106dfSjm  * Attempt to do a non-blocking connect call.
749911106dfSjm  * Wait for a maximum of "vs_connect_timeout" millisec, then check for
750911106dfSjm  * socket error to determine if connect successful or not.
751911106dfSjm  */
752911106dfSjm #ifdef FIONBIO
753911106dfSjm static int
nbio_connect(int sockfd,const struct sockaddr * sa,int sa_len)754bfc848c6Sjm nbio_connect(int sockfd, const struct sockaddr *sa, int sa_len)
755911106dfSjm {
756911106dfSjm 	struct pollfd pfd;
757911106dfSjm 	int nbio, rc;
758911106dfSjm 	int error, len = sizeof (error);
759911106dfSjm 
760911106dfSjm 	nbio = 1;
761bfc848c6Sjm 	if ((ioctl(sockfd, FIONBIO, &nbio)) < 0)
762bfc848c6Sjm 		return (connect(sockfd, sa, sa_len));
763911106dfSjm 
764bfc848c6Sjm 	if ((rc = connect(sockfd, sa, sa_len)) != 0) {
765911106dfSjm 		if (errno == EINPROGRESS || errno == EINTR) {
766bfc848c6Sjm 			errno = 0;
767bfc848c6Sjm 			pfd.fd = sockfd;
768911106dfSjm 			pfd.events = POLLOUT;
769911106dfSjm 			pfd.revents = 0;
770911106dfSjm 
771911106dfSjm 			if ((rc = poll(&pfd, 1, vs_connect_timeout)) <= 0) {
772911106dfSjm 				if (rc == 0)
773911106dfSjm 					errno = ETIMEDOUT;
774911106dfSjm 				rc = -1;
775911106dfSjm 			} else {
776bfc848c6Sjm 				if ((pfd.revents &
777bfc848c6Sjm 				    (POLLHUP | POLLERR | POLLNVAL)) ||
778bfc848c6Sjm 				    (!(pfd.revents & POLLOUT))) {
779911106dfSjm 					rc = -1;
780bfc848c6Sjm 				} else {
781bfc848c6Sjm 					rc = getsockopt(sockfd, SOL_SOCKET,
782bfc848c6Sjm 					    SO_ERROR, &error, &len);
783bfc848c6Sjm 					if (rc != 0 || error != 0)
784bfc848c6Sjm 						rc = -1;
785bfc848c6Sjm 					if (error != 0)
786bfc848c6Sjm 						errno = error;
787bfc848c6Sjm 				}
788911106dfSjm 			}
789911106dfSjm 		}
790911106dfSjm 	}
791911106dfSjm 
792911106dfSjm 	nbio = 0;
793bfc848c6Sjm 	(void) ioctl(sockfd, FIONBIO, &nbio);
794911106dfSjm 
795911106dfSjm 	return (rc);
796911106dfSjm }
797911106dfSjm #endif
798911106dfSjm 
799911106dfSjm 
800911106dfSjm /*
801911106dfSjm  * vs_eng_scanstamp_current
802911106dfSjm  *
803911106dfSjm  * Check if scanstamp matches that of ANY engine with no errors.
804911106dfSjm  * We cannot include engines with errors as they may have been
805911106dfSjm  * inaccessible for a long time and thus we may have an old
806911106dfSjm  * scanstamp value for them.
807911106dfSjm  * If a match is found the scanstamp is considered to be current
808911106dfSjm  *
809911106dfSjm  * returns: 1 if current, 0 otherwise
810911106dfSjm  */
811911106dfSjm int
vs_eng_scanstamp_current(vs_scanstamp_t scanstamp)812911106dfSjm vs_eng_scanstamp_current(vs_scanstamp_t scanstamp)
813911106dfSjm {
814911106dfSjm 	int i;
815911106dfSjm 
816911106dfSjm 	/* if scan stamp is null, not current */
817911106dfSjm 	if (scanstamp[0] == '\0')
818911106dfSjm 		return (0);
819911106dfSjm 
82053c11029Sjm 	/* if scanstamp matches that of any enabled engine with no errors */
821911106dfSjm 	(void) pthread_mutex_lock(&vs_eng_mutex);
822911106dfSjm 	for (i = 0; i < VS_SE_MAX; i++) {
82353c11029Sjm 		if ((vs_engines[i].vse_cfg.vep_enable) &&
824bfc848c6Sjm 		    (vs_engines[i].vse_error == B_FALSE) &&
825911106dfSjm 		    (vs_icap_compare_scanstamp(i, scanstamp) == 0))
826911106dfSjm 			break;
827911106dfSjm 	}
828911106dfSjm 	(void) pthread_mutex_unlock(&vs_eng_mutex);
829911106dfSjm 
830911106dfSjm 	return ((i < VS_SE_MAX) ? 1 : 0);
831911106dfSjm }
832911106dfSjm 
833911106dfSjm 
834911106dfSjm /*
835911106dfSjm  * vs_eng_compare
836911106dfSjm  * compare host and port with that stored for engine idx
837911106dfSjm  *
838911106dfSjm  * Returns: 0 - if equal
839911106dfSjm  */
840911106dfSjm static int
vs_eng_compare(int idx,char * host,int port)841911106dfSjm vs_eng_compare(int idx, char *host, int port)
842911106dfSjm {
843911106dfSjm 	if (vs_engines[idx].vse_cfg.vep_port != port)
844911106dfSjm 		return (-1);
845911106dfSjm 
846911106dfSjm 	if (strcmp(vs_engines[idx].vse_cfg.vep_host, host) != 0)
847911106dfSjm 		return (-1);
848911106dfSjm 
849911106dfSjm 	return (0);
850911106dfSjm }
851