xref: /illumos-gate/usr/src/cmd/vscan/vscand/vs_eng.c (revision 5422785d352a2bb398daceab3d1898a8aa64d006)
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 <sys/time.h>
42 #include <netinet/in.h>
43 #include <netinet/tcp.h>
44 #include <arpa/inet.h>
45 #include <unistd.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <syslog.h>
49 #include <errno.h>
50 #include <poll.h>
51 #include <pthread.h>
52 #include <time.h>
53 
54 #include <signal.h>
55 #include <thread.h>
56 
57 #include "vs_incl.h"
58 
59 /* max connections per scan engine */
60 #define	VS_CXN_MAX	VS_VAL_SE_MAXCONN_MAX
61 
62 /*
63  * vs_eng_state_t - connection state
64  *
65  * Each configured scan engine supports up to vse_cfg.vep_maxconn
66  * connections. These connections are represented by a vs_connection_t
67  * which defines the connection state, associated socket descriptor
68  * and how long the connection has been available. A connection
69  * that has been available but unused for vs_inactivity_timeout
70  * seconds will be closed by the housekeeper thread.
71  *
72  * When a scan engine is reconfigured to have less connections
73  * (or is disabled) any of he superflous connections which are in
74  * AVAILABLE state are closed (DISCONNECTED). Others are set to
75  * CLOSE_PENDING to be closed (DISCONNECTED) when the engine is
76  * released (when the current request completes).
77  *
78  *              +---------------------+
79  *  |---------->| VS_ENG_DISCONNECTED |<-----------------|
80  *  |           +---------------------+                  |
81  *  |              |                                     |
82  *  |              | eng_get                             |
83  *  |              v                                     | release/
84  *  | shutdown  +---------------------+   reconfig       | shutdown
85  *  |<----------| VS_ENG_RESERVED     | -----------|     |
86  *  |           +---------------------+            |     |
87  *  |              |                               v     |
88  *  |              |                       +----------------------+
89  *  |              | connect               | VS_ENG_CLOSE_PENDING |
90  *  |              |                       +----------------------+
91  *  |              v                               ^
92  *  | shutdown  +---------------------+            |
93  *  |<----------| VS_ENG_INUSE        |------------|
94  *  |           +---------------------+  reconfig/error
95  *  |              |           ^
96  *  |              | release   | eng_get
97  *  | reconfig/    |           |
98  *  | timeout/     v           |
99  *  | shutdown  +---------------------+
100  *  |<----------| VS_ENG_AVAILABLE    |
101  *              +---------------------+
102  *
103  */
104 
105 typedef enum {
106 	VS_ENG_DISCONNECTED = 0,
107 	VS_ENG_RESERVED,
108 	VS_ENG_INUSE,
109 	VS_ENG_AVAILABLE,
110 	VS_ENG_CLOSE_PENDING
111 } vs_eng_state_t;
112 
113 typedef struct vs_connection {
114 	vs_eng_state_t vsc_state;
115 	int vsc_sockfd;
116 	struct timeval vsc_avail_time;
117 } vs_connection_t;
118 
119 typedef struct vs_engine {
120 	vs_props_se_t vse_cfg;	/* host, port, maxconn */
121 	int vse_inuse;		/* # connections in use */
122 	boolean_t vse_error;
123 	vs_connection_t vse_cxns[VS_CXN_MAX];
124 } vs_engine_t;
125 
126 static vs_engine_t vs_engines[VS_SE_MAX];
127 
128 static int vs_eng_next;		/* round-robin "finger" */
129 static int vs_eng_count;	/* how many configured engines */
130 static int vs_eng_total_maxcon;	/* total configured connections */
131 static int vs_eng_total_inuse;	/* total connections in use */
132 static int vs_eng_wait_count;	/* # threads waiting for connection */
133 
134 static pthread_mutex_t vs_eng_mutex = PTHREAD_MUTEX_INITIALIZER;
135 static pthread_cond_t vs_eng_cv;
136 int vs_inactivity_timeout = 60; /* seconds */
137 int vs_reuse_connection = 1;
138 
139 time_t vs_eng_wait = VS_ENG_WAIT_DFLT;
140 
141 /* local functions */
142 static int vs_eng_connect(char *, int);
143 static boolean_t vs_eng_check_errors(void);
144 static int vs_eng_find_connection(int *, int *, boolean_t);
145 static int vs_eng_find_next(boolean_t);
146 static int vs_eng_compare(int, char *, int);
147 static void vs_eng_config_close(vs_engine_t *, int);
148 static void *vs_eng_housekeeper(void *);
149 
150 
151 #ifdef FIONBIO
152 /* non-blocking connect */
153 static int nbio_connect(int, const struct sockaddr *, int);
154 int vs_connect_timeout = 5000; /* milliseconds */
155 #endif /* FIONBIO */
156 
157 
158 /*
159  * vs_eng_init
160  */
161 void
162 vs_eng_init()
163 {
164 	pthread_t tid;
165 
166 	(void) pthread_cond_init(&vs_eng_cv, NULL);
167 	(void) pthread_mutex_lock(&vs_eng_mutex);
168 
169 	(void) memset(vs_engines, 0, sizeof (vs_engine_t) * VS_SE_MAX);
170 
171 	vs_eng_total_maxcon = 0;
172 	vs_eng_total_inuse = 0;
173 	vs_eng_count = 0;
174 	vs_eng_next = 0;
175 
176 	(void) pthread_mutex_unlock(&vs_eng_mutex);
177 
178 	(void) pthread_create(&tid, NULL, vs_eng_housekeeper, NULL);
179 }
180 
181 
182 /*
183  * vs_eng_config
184  *
185  * Configure scan engine connections.
186  *
187  * If a scan engine has been reconfigured (different host or port)
188  * the scan engine's error count is reset.
189  *
190  * If the host/port has changed, the engine has been disabled
191  * or less connections are configured now, connections need
192  * to be closed or placed in CLOSE_PENDING state (vs_eng_config_close)
193  *
194  * vs_icap_config is invoked to reset engine-specific data stored
195  * in vs_icap.
196  *
197  */
198 void
199 vs_eng_config(vs_props_all_t *config)
200 {
201 	int i;
202 	vs_props_se_t *cfg;
203 	vs_engine_t *eng;
204 
205 	(void) pthread_mutex_lock(&vs_eng_mutex);
206 
207 	vs_eng_count = 0;
208 	vs_eng_total_maxcon = 0;
209 
210 	for (i = 0; i < VS_SE_MAX; i++) {
211 		cfg = &config->va_se[i];
212 		eng = &vs_engines[i];
213 
214 		if (vs_eng_compare(i, cfg->vep_host, cfg->vep_port) != 0) {
215 			vs_eng_config_close(eng, 0);
216 			eng->vse_error = B_FALSE;
217 		}
218 
219 		if (cfg->vep_enable) {
220 			if (cfg->vep_maxconn < eng->vse_cfg.vep_maxconn)
221 				vs_eng_config_close(eng, cfg->vep_maxconn);
222 
223 			eng->vse_cfg = *cfg;
224 			vs_eng_total_maxcon += cfg->vep_maxconn;
225 			vs_eng_count++;
226 		} else {
227 			vs_eng_config_close(eng, 0);
228 			(void) memset(&eng->vse_cfg, 0, sizeof (vs_props_se_t));
229 		}
230 
231 		vs_icap_config(i, eng->vse_cfg.vep_host, eng->vse_cfg.vep_port);
232 	}
233 
234 	if ((vs_eng_total_maxcon <= 0) || (vs_eng_count == 0))
235 		syslog(LOG_NOTICE, "Scan Engine - no engines configured");
236 
237 	(void) pthread_mutex_unlock(&vs_eng_mutex);
238 }
239 
240 
241 /*
242  * vs_eng_config_close
243  *
244  *	If the host/port has changed, the engine has been disabled
245  *	or less connections are configured now, connections need
246  *	to be closed or placed in CLOSE_PENDING state
247  */
248 static void
249 vs_eng_config_close(vs_engine_t *eng, int start_idx)
250 {
251 	int i;
252 	vs_connection_t *cxn;
253 
254 	for (i = start_idx; i < eng->vse_cfg.vep_maxconn; i++) {
255 		cxn = &(eng->vse_cxns[i]);
256 
257 		switch (cxn->vsc_state) {
258 		case VS_ENG_RESERVED:
259 		case VS_ENG_INUSE:
260 			cxn->vsc_state = VS_ENG_CLOSE_PENDING;
261 			break;
262 		case VS_ENG_AVAILABLE:
263 			(void) close(cxn->vsc_sockfd);
264 			cxn->vsc_sockfd = -1;
265 			cxn->vsc_state = VS_ENG_DISCONNECTED;
266 			break;
267 		case VS_ENG_CLOSE_PENDING:
268 		case VS_ENG_DISCONNECTED:
269 			break;
270 		}
271 	}
272 }
273 
274 
275 /*
276  * vs_eng_fini
277  */
278 void
279 vs_eng_fini()
280 {
281 	(void) pthread_cond_destroy(&vs_eng_cv);
282 }
283 
284 
285 /*
286  * vs_eng_housekeeper
287  *
288  * Wakeup every (vs_inactivity_timeout / 2) seconds and close
289  * any connections that are in AVAILABLE state but have not
290  * been used for vs_inactivity_timeout seconds.
291  */
292 /* ARGSUSED */
293 static void *
294 vs_eng_housekeeper(void *arg)
295 {
296 	struct timeval now;
297 	long expire;
298 	int i, j;
299 	vs_engine_t *eng;
300 	vs_connection_t *cxn;
301 
302 	for (;;) {
303 		(void) sleep(vs_inactivity_timeout / 2);
304 
305 		if (vscand_get_state() == VS_STATE_SHUTDOWN)
306 			break;
307 
308 		(void) gettimeofday(&now, NULL);
309 		expire = now.tv_sec - vs_inactivity_timeout;
310 
311 		(void) pthread_mutex_lock(&vs_eng_mutex);
312 		for (i = 0; i < VS_SE_MAX; i++) {
313 			eng = &(vs_engines[i]);
314 			for (j = 0; j < eng->vse_cfg.vep_maxconn; j++) {
315 				cxn = &(eng->vse_cxns[j]);
316 
317 				if ((cxn->vsc_state == VS_ENG_AVAILABLE) &&
318 				    (cxn->vsc_avail_time.tv_sec < expire)) {
319 					(void) close(cxn->vsc_sockfd);
320 					cxn->vsc_sockfd = -1;
321 					cxn->vsc_state = VS_ENG_DISCONNECTED;
322 				}
323 			}
324 		}
325 		(void) pthread_mutex_unlock(&vs_eng_mutex);
326 	}
327 
328 	return (NULL);
329 }
330 
331 
332 /*
333  * vs_eng_set_error
334  *
335  * If the engine identified in conn (host, port) matches the
336  * engine in vs_engines set or clear the error state of the
337  * engine and update the error statistics.
338  *
339  * If error == 0, clear the error state(B_FALSE), else set
340  * the error state (B_TRUE) and increment engine error stats
341  */
342 void
343 vs_eng_set_error(vs_eng_ctx_t *eng_ctx, int error)
344 {
345 	int eidx = eng_ctx->vse_eidx;
346 	int cidx =  eng_ctx->vse_cidx;
347 	vs_engine_t *eng;
348 
349 	(void) pthread_mutex_lock(&vs_eng_mutex);
350 
351 	eng = &(vs_engines[eidx]);
352 
353 	if (vs_eng_compare(eidx, eng_ctx->vse_host, eng_ctx->vse_port) == 0)
354 		eng->vse_error = (error == 0) ? B_FALSE : B_TRUE;
355 
356 	if (error != 0) {
357 		eng->vse_cxns[cidx].vsc_state = VS_ENG_CLOSE_PENDING;
358 		vs_stats_eng_err(eng_ctx->vse_engid);
359 	}
360 
361 	(void) pthread_mutex_unlock(&vs_eng_mutex);
362 }
363 
364 
365 /*
366  * vs_eng_get
367  * Get next available scan engine connection.
368  * If retry == B_TRUE look for a scan engine with no errors.
369  *
370  * Returns: 0 - success
371  *         -1 - error
372  */
373 int
374 vs_eng_get(vs_eng_ctx_t *eng_ctx, boolean_t retry)
375 {
376 	struct timespec tswait;
377 	int eidx, cidx, sockfd;
378 	vs_engine_t *eng;
379 	vs_connection_t *cxn;
380 
381 	(void) pthread_mutex_lock(&vs_eng_mutex);
382 
383 	/*
384 	 * If no engines connections configured OR
385 	 * retry and only one engine configured, give up
386 	 */
387 	if ((vs_eng_total_maxcon <= 0) ||
388 	    ((retry == B_TRUE) && (vs_eng_count <= 1))) {
389 		(void) pthread_mutex_unlock(&vs_eng_mutex);
390 		return (-1);
391 	}
392 
393 	tswait.tv_sec = vs_eng_wait;
394 	tswait.tv_nsec = 0;
395 
396 	while ((vscand_get_state() != VS_STATE_SHUTDOWN) &&
397 	    (vs_eng_find_connection(&eidx, &cidx, retry) == -1)) {
398 		/* If retry and all configured engines have errors, give up */
399 		if (retry && vs_eng_check_errors() == B_TRUE) {
400 			(void) pthread_mutex_unlock(&vs_eng_mutex);
401 			return (-1);
402 		}
403 
404 		/* wait for a connection to become available */
405 		vs_eng_wait_count++;
406 		if (pthread_cond_reltimedwait_np(&vs_eng_cv, &vs_eng_mutex,
407 		    &tswait) < 0) {
408 			syslog(LOG_NOTICE, "Scan Engine "
409 			    "- timeout waiting for available engine");
410 			vs_eng_wait_count--;
411 			(void) pthread_mutex_unlock(&vs_eng_mutex);
412 			return (-1);
413 		}
414 		vs_eng_wait_count--;
415 	}
416 
417 	if (vscand_get_state() == VS_STATE_SHUTDOWN) {
418 		(void) pthread_mutex_unlock(&vs_eng_mutex);
419 		return (-1);
420 	}
421 
422 	eng = &(vs_engines[eidx]);
423 	cxn = &(eng->vse_cxns[cidx]);
424 
425 	/* update in use counts */
426 	eng->vse_inuse++;
427 	vs_eng_total_inuse++;
428 
429 	/* update round-robin index */
430 	if (!retry)
431 		vs_eng_next = (eidx == VS_SE_MAX) ? 0 : eidx + 1;
432 
433 	/* populate vs_eng_ctx_t */
434 	eng_ctx->vse_eidx = eidx;
435 	eng_ctx->vse_cidx = cidx;
436 	(void) strlcpy(eng_ctx->vse_engid, eng->vse_cfg.vep_engid,
437 	    sizeof (eng_ctx->vse_engid));
438 	(void) strlcpy(eng_ctx->vse_host, eng->vse_cfg.vep_host,
439 	    sizeof (eng_ctx->vse_host));
440 	eng_ctx->vse_port = eng->vse_cfg.vep_port;
441 	eng_ctx->vse_sockfd = cxn->vsc_sockfd;
442 
443 	if (cxn->vsc_state == VS_ENG_INUSE) {
444 		(void) pthread_mutex_unlock(&vs_eng_mutex);
445 		return (0);
446 	}
447 
448 	/* state == VS_ENG_RESERVED, need to connect */
449 
450 	(void) pthread_mutex_unlock(&vs_eng_mutex);
451 
452 	sockfd = vs_eng_connect(eng_ctx->vse_host, eng_ctx->vse_port);
453 
454 	/* retry a failed connection once */
455 	if (sockfd == -1) {
456 		(void) sleep(1);
457 		sockfd = vs_eng_connect(eng_ctx->vse_host, eng_ctx->vse_port);
458 	}
459 
460 	if (sockfd == -1) {
461 		syslog(LOG_NOTICE, "Scan Engine - connection error (%s:%d) %s",
462 		    eng_ctx->vse_host, eng_ctx->vse_port,
463 		    errno ? strerror(errno) : "");
464 		vs_eng_set_error(eng_ctx, 1);
465 		vs_eng_release(eng_ctx);
466 		return (-1);
467 	}
468 
469 	(void) pthread_mutex_lock(&vs_eng_mutex);
470 	switch (cxn->vsc_state) {
471 	case VS_ENG_DISCONNECTED:
472 		/* SHUTDOWN occured */
473 		(void) pthread_mutex_unlock(&vs_eng_mutex);
474 		vs_eng_release(eng_ctx);
475 		return (-1);
476 	case VS_ENG_RESERVED:
477 		cxn->vsc_state = VS_ENG_INUSE;
478 		break;
479 	case VS_ENG_CLOSE_PENDING:
480 		/* reconfigure occured. Connection will be closed after use */
481 		break;
482 	case VS_ENG_INUSE:
483 	case VS_ENG_AVAILABLE:
484 	default:
485 		ASSERT(0);
486 		break;
487 	}
488 
489 	cxn->vsc_sockfd = sockfd;
490 	eng_ctx->vse_sockfd = sockfd;
491 
492 	(void) pthread_mutex_unlock(&vs_eng_mutex);
493 	return (0);
494 }
495 
496 
497 /*
498  * vs_eng_check_errors
499  *
500  * Check if all engines with maxconn > 0 are in error state
501  *
502  * Returns: B_TRUE  - all (valid) engines are in error state
503  *          B_FALSE - otherwise
504  */
505 static boolean_t
506 vs_eng_check_errors()
507 {
508 	int i;
509 
510 	for (i = 0; i < VS_SE_MAX; i++) {
511 		if (vs_engines[i].vse_cfg.vep_maxconn > 0 &&
512 		    (vs_engines[i].vse_error == B_FALSE))
513 			return (B_FALSE);
514 	}
515 
516 	return (B_TRUE);
517 }
518 
519 
520 /*
521  * vs_eng_find_connection
522  *
523  * Identify the next engine to be used (vs_eng_find_next()).
524  * Select the engine's first connection in AVAILABLE state.
525  * If no connection is in AVAILABLE state, select the first
526  * that is in DISCONNECTED state.
527  *
528  * Returns: 0 success
529  *         -1 no engine connections available (eng_idx & cxn_idx undefined)
530  */
531 static int
532 vs_eng_find_connection(int *eng_idx, int *cxn_idx, boolean_t retry)
533 {
534 	int i, idx;
535 	vs_engine_t *eng;
536 	vs_connection_t *cxn;
537 
538 	/* identify engine */
539 	if ((idx = vs_eng_find_next(retry)) == -1)
540 		return (-1);
541 
542 	eng = &(vs_engines[idx]);
543 	*eng_idx = idx;
544 
545 	/* identify connection */
546 	idx = -1;
547 	for (i = 0; i < eng->vse_cfg.vep_maxconn; i++) {
548 		cxn = &(eng->vse_cxns[i]);
549 		if (cxn->vsc_state == VS_ENG_AVAILABLE) {
550 			*cxn_idx = i;
551 			cxn->vsc_state = VS_ENG_INUSE;
552 			return (0);
553 		}
554 
555 		if ((idx == -1) &&
556 		    (cxn->vsc_state == VS_ENG_DISCONNECTED)) {
557 			idx = i;
558 		}
559 	}
560 
561 	if (idx == -1)
562 		return (-1);
563 
564 	eng->vse_cxns[idx].vsc_state = VS_ENG_RESERVED;
565 	*cxn_idx = idx;
566 	return (0);
567 }
568 
569 
570 /*
571  * vs_eng_find_next
572  *
573  * Returns: -1 no engine connections available
574  *          idx of engine to use
575  */
576 static int
577 vs_eng_find_next(boolean_t retry)
578 {
579 	int i;
580 
581 	for (i = vs_eng_next; i < VS_SE_MAX; i++) {
582 		if (vs_engines[i].vse_inuse <
583 		    vs_engines[i].vse_cfg.vep_maxconn) {
584 			if (!retry || (vs_engines[i].vse_error == B_FALSE))
585 				return (i);
586 		}
587 	}
588 
589 	for (i = 0; i < vs_eng_next; i++) {
590 		if (vs_engines[i].vse_inuse <
591 		    vs_engines[i].vse_cfg.vep_maxconn) {
592 			if (!retry || (vs_engines[i].vse_error == B_FALSE))
593 				return (i);
594 		}
595 	}
596 
597 	return (-1);
598 }
599 
600 
601 /*
602  * vs_eng_release
603  */
604 void
605 vs_eng_release(const vs_eng_ctx_t *eng_ctx)
606 {
607 	int eidx = eng_ctx->vse_eidx;
608 	int cidx = eng_ctx->vse_cidx;
609 	vs_connection_t *cxn;
610 
611 	(void) pthread_mutex_lock(&vs_eng_mutex);
612 	cxn = &(vs_engines[eidx].vse_cxns[cidx]);
613 
614 	switch (cxn->vsc_state) {
615 	case VS_ENG_DISCONNECTED:
616 		break;
617 	case VS_ENG_RESERVED:
618 		cxn->vsc_state = VS_ENG_DISCONNECTED;
619 		break;
620 	case VS_ENG_INUSE:
621 		if (vs_reuse_connection) {
622 			cxn->vsc_state = VS_ENG_AVAILABLE;
623 			(void) gettimeofday(&cxn->vsc_avail_time, NULL);
624 			break;
625 		}
626 		/* LINTED E_CASE_FALL_THROUGH - close connection */
627 	case VS_ENG_CLOSE_PENDING:
628 		(void) close(cxn->vsc_sockfd);
629 		cxn->vsc_sockfd = -1;
630 		cxn->vsc_state = VS_ENG_DISCONNECTED;
631 		break;
632 	case VS_ENG_AVAILABLE:
633 	default:
634 		ASSERT(0);
635 		break;
636 	}
637 
638 	/* decrement in use counts */
639 	vs_engines[eidx].vse_inuse--;
640 	vs_eng_total_inuse--;
641 
642 	/* wake up next thread waiting for a connection */
643 	(void) pthread_cond_signal(&vs_eng_cv);
644 
645 	(void) pthread_mutex_unlock(&vs_eng_mutex);
646 }
647 
648 
649 /*
650  * vs_eng_close_connections
651  *
652  * Set vs_eng_total_maxcon to 0 to ensure no new engine sessions
653  * can be initiated.
654  * Close all open connections to abort in-progress scans.
655  * Set connection state to DISCONNECTED.
656  */
657 void
658 vs_eng_close_connections(void)
659 {
660 	int i, j;
661 	vs_connection_t *cxn;
662 
663 	(void) pthread_mutex_lock(&vs_eng_mutex);
664 	vs_eng_total_maxcon = 0;
665 
666 	for (i = 0; i < VS_SE_MAX; i++) {
667 		for (j = 0; j < VS_CXN_MAX; j++) {
668 			cxn = &(vs_engines[i].vse_cxns[j]);
669 
670 			switch (cxn->vsc_state) {
671 			case VS_ENG_INUSE:
672 			case VS_ENG_AVAILABLE:
673 			case VS_ENG_CLOSE_PENDING:
674 				(void) close(cxn->vsc_sockfd);
675 				cxn->vsc_sockfd = -1;
676 				break;
677 			case VS_ENG_DISCONNECTED:
678 			case VS_ENG_RESERVED:
679 			default:
680 				break;
681 
682 			}
683 
684 			cxn->vsc_state = VS_ENG_DISCONNECTED;
685 		}
686 	}
687 	(void) pthread_mutex_unlock(&vs_eng_mutex);
688 }
689 
690 
691 /*
692  * vs_eng_connect
693  * open socket connection to remote scan engine
694  *
695  * Returns: sockfd or -1 (error)
696  */
697 static int
698 vs_eng_connect(char *host, int port)
699 {
700 	int rc, sockfd, opt_nodelay, opt_keepalive, opt_reuseaddr, err_num;
701 	struct sockaddr_in addr;
702 	struct hostent *hp;
703 
704 	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
705 		return (-1);
706 
707 	hp = getipnodebyname(host, AF_INET, 0, &err_num);
708 	if (hp == NULL) {
709 		(void) close(sockfd);
710 		return (-1);
711 	}
712 
713 	(void) memset(&addr, 0, sizeof (addr));
714 	(void) memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
715 	addr.sin_port = htons(port);
716 	addr.sin_family = hp->h_addrtype;
717 	freehostent(hp);
718 
719 #ifdef FIONBIO /* Use non-blocking mode for connect. */
720 	rc = nbio_connect(sockfd, (struct sockaddr *)&addr,
721 	    sizeof (struct sockaddr));
722 #else
723 	rc = connect(sockfd, (struct sockaddr *)&addr,
724 	    sizeof (struct sockaddr));
725 #endif
726 
727 	opt_nodelay = 1;
728 	opt_keepalive = 1;
729 	opt_reuseaddr = 1;
730 
731 	if ((rc < 0) ||
732 	    (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY,
733 	    &opt_nodelay, sizeof (opt_nodelay)) < 0) ||
734 	    (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE,
735 	    &opt_keepalive, sizeof (opt_keepalive)) < 0) ||
736 	    (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
737 	    &opt_reuseaddr, sizeof (opt_reuseaddr)) < 0)) {
738 		(void) close(sockfd);
739 		return (-1);
740 	}
741 
742 	return (sockfd);
743 }
744 
745 
746 /*
747  * nbio_connect
748  *
749  * Attempt to do a non-blocking connect call.
750  * Wait for a maximum of "vs_connect_timeout" millisec, then check for
751  * socket error to determine if connect successful or not.
752  */
753 #ifdef FIONBIO
754 static int
755 nbio_connect(int sockfd, const struct sockaddr *sa, int sa_len)
756 {
757 	struct pollfd pfd;
758 	int nbio, rc;
759 	int error, len = sizeof (error);
760 
761 	nbio = 1;
762 	if ((ioctl(sockfd, FIONBIO, &nbio)) < 0)
763 		return (connect(sockfd, sa, sa_len));
764 
765 	if ((rc = connect(sockfd, sa, sa_len)) != 0) {
766 		if (errno == EINPROGRESS || errno == EINTR) {
767 			errno = 0;
768 			pfd.fd = sockfd;
769 			pfd.events = POLLOUT;
770 			pfd.revents = 0;
771 
772 			if ((rc = poll(&pfd, 1, vs_connect_timeout)) <= 0) {
773 				if (rc == 0)
774 					errno = ETIMEDOUT;
775 				rc = -1;
776 			} else {
777 				if ((pfd.revents &
778 				    (POLLHUP | POLLERR | POLLNVAL)) ||
779 				    (!(pfd.revents & POLLOUT))) {
780 					rc = -1;
781 				} else {
782 					rc = getsockopt(sockfd, SOL_SOCKET,
783 					    SO_ERROR, &error, &len);
784 					if (rc != 0 || error != 0)
785 						rc = -1;
786 					if (error != 0)
787 						errno = error;
788 				}
789 			}
790 		}
791 	}
792 
793 	nbio = 0;
794 	(void) ioctl(sockfd, FIONBIO, &nbio);
795 
796 	return (rc);
797 }
798 #endif
799 
800 
801 /*
802  * vs_eng_scanstamp_current
803  *
804  * Check if scanstamp matches that of ANY engine with no errors.
805  * We cannot include engines with errors as they may have been
806  * inaccessible for a long time and thus we may have an old
807  * scanstamp value for them.
808  * If a match is found the scanstamp is considered to be current
809  *
810  * returns: 1 if current, 0 otherwise
811  */
812 int
813 vs_eng_scanstamp_current(vs_scanstamp_t scanstamp)
814 {
815 	int i;
816 
817 	/* if scan stamp is null, not current */
818 	if (scanstamp[0] == '\0')
819 		return (0);
820 
821 	/* if scanstamp matches that of any enabled engine with no errors */
822 	(void) pthread_mutex_lock(&vs_eng_mutex);
823 	for (i = 0; i < VS_SE_MAX; i++) {
824 		if ((vs_engines[i].vse_cfg.vep_enable) &&
825 		    (vs_engines[i].vse_error == B_FALSE) &&
826 		    (vs_icap_compare_scanstamp(i, scanstamp) == 0))
827 			break;
828 	}
829 	(void) pthread_mutex_unlock(&vs_eng_mutex);
830 
831 	return ((i < VS_SE_MAX) ? 1 : 0);
832 }
833 
834 
835 /*
836  * vs_eng_compare
837  * compare host and port with that stored for engine idx
838  *
839  * Returns: 0 - if equal
840  */
841 static int
842 vs_eng_compare(int idx, char *host, int port)
843 {
844 	if (vs_engines[idx].vse_cfg.vep_port != port)
845 		return (-1);
846 
847 	if (strcmp(vs_engines[idx].vse_cfg.vep_host, host) != 0)
848 		return (-1);
849 
850 	return (0);
851 }
852