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