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