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
vs_eng_init()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
vs_eng_config(vs_props_all_t * config)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
vs_eng_config_close(vs_engine_t * eng,int start_idx)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
vs_eng_fini()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 *
vs_eng_housekeeper(void * arg)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
vs_eng_set_error(vs_eng_ctx_t * eng_ctx,int error)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
vs_eng_get(vs_eng_ctx_t * eng_ctx,boolean_t retry)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
vs_eng_check_errors()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
vs_eng_find_connection(int * eng_idx,int * cxn_idx,boolean_t retry)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
vs_eng_find_next(boolean_t retry)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
vs_eng_release(const vs_eng_ctx_t * eng_ctx)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
vs_eng_close_connections(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
vs_eng_connect(char * host,int port)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
nbio_connect(int sockfd,const struct sockaddr * sa,int sa_len)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
vs_eng_scanstamp_current(vs_scanstamp_t scanstamp)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
vs_eng_compare(int idx,char * host,int port)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