xref: /illumos-gate/usr/src/cmd/vscan/vscand/vs_eng.c (revision 936b7af69172dce89b577831f79c0e18d15e854b)
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 2007 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 <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 "vs_incl.h"
54 
55 
56 typedef struct vs_engine {
57 	vs_props_se_t vse_cfg;	/* host, port, maxcon */
58 	int vse_in_use;	/* # connections in use */
59 	int vse_error;
60 	vs_eng_conn_t vse_conn_root;
61 } vs_engine_t;
62 
63 static vs_engine_t vs_engines[VS_SE_MAX];
64 
65 static int vs_eng_next;		/* round-robin "finger" */
66 static int vs_eng_count;	/* how many configured engines */
67 static int vs_eng_total_maxcon;	/* total configured connections */
68 static int vs_eng_total_inuse;	/* total connections in use */
69 static int vs_eng_wait_count;	/* # threads waiting for connection */
70 
71 static pthread_mutex_t vs_eng_mutex = PTHREAD_MUTEX_INITIALIZER;
72 static pthread_cond_t vs_eng_cv;
73 static pthread_cond_t vs_eng_shutdown_cv;
74 
75 static time_t vs_eng_wait = VS_ENG_WAIT_DFLT;
76 
77 /* local functions */
78 static int vs_eng_check_errors(void);
79 static int vs_eng_find_next(int);
80 static void vs_eng_add_connection(vs_eng_conn_t *);
81 static void vs_eng_remove_connection(vs_eng_conn_t *);
82 static void vs_eng_close_connections(void);
83 static int vs_eng_compare(int, char *, int);
84 
85 
86 #ifdef FIONBIO
87 /* non-blocking connect */
88 static int nbio_connect(vs_eng_conn_t *, const struct sockaddr *, int);
89 int vs_connect_timeout = 5000; /* milliseconds */
90 #endif /* FIONBIO */
91 
92 
93 /*
94  * vs_eng_init
95  */
96 void
97 vs_eng_init()
98 {
99 	(void) pthread_cond_init(&vs_eng_cv, NULL);
100 	(void) pthread_cond_init(&vs_eng_shutdown_cv, NULL);
101 	(void) pthread_mutex_lock(&vs_eng_mutex);
102 
103 	(void) memset(vs_engines, 0,
104 	    sizeof (vs_engine_t) * VS_SE_MAX);
105 	vs_eng_total_maxcon = 0;
106 	vs_eng_total_inuse = 0;
107 	vs_eng_count = 0;
108 	vs_eng_next = 0;
109 
110 	(void) pthread_mutex_unlock(&vs_eng_mutex);
111 }
112 
113 
114 /*
115  * vs_eng_config
116  *
117  * Configure scan engine connections.
118  *
119  * The enable property is the single indicator that the engine
120  * configuration is valid. Configuration is guaranteed (by the
121  * library) to be consistent; if the host is invalid or unconfigured,
122  * the enable setting will always be false.
123  *
124  * If a scan engine has been reconfigured (different host or port)
125  * the scan engine's error count and statitics are reset, and
126  * vs_icap_config is invoked to reset engine-specific data stored
127  * in vs_icap
128  *
129  */
130 void
131 vs_eng_config(vs_props_all_t *config)
132 {
133 	int i;
134 	vs_props_se_t *cfg;
135 
136 	(void) pthread_mutex_lock(&vs_eng_mutex);
137 
138 	vs_eng_count = 0;
139 	vs_eng_total_maxcon = 0;
140 
141 	for (i = 0; i < VS_SE_MAX; i++) {
142 		cfg = &config->va_se[i];
143 
144 		if (vs_eng_compare(i, cfg->vep_host, cfg->vep_port) != 0) {
145 			vs_engines[i].vse_error = 0;
146 			vs_icap_config(i, cfg->vep_host, cfg->vep_port);
147 		}
148 
149 		if (cfg->vep_enable) {
150 			vs_engines[i].vse_cfg = *cfg;
151 			vs_eng_total_maxcon += cfg->vep_maxconn;
152 			vs_eng_count++;
153 		} else {
154 			(void) memset(&vs_engines[i].vse_cfg, 0,
155 			    sizeof (vs_props_se_t));
156 		}
157 
158 	}
159 
160 	if ((vs_eng_total_maxcon <= 0) || (vs_eng_count == 0))
161 		syslog(LOG_WARNING, "Scan Engine - no engines configured");
162 
163 	(void) pthread_mutex_unlock(&vs_eng_mutex);
164 }
165 
166 
167 /*
168  * vs_eng_fini
169  *
170  * Close all scan engine connections to abort in-progress scans,
171  * and wait until all to sessions are complete, and there are no
172  * waiting threads.
173  * Set vs_eng_total_maxcon to 0 to ensure no new engine sessions
174  * can be initiated while we're waiting.
175  */
176 void
177 vs_eng_fini()
178 {
179 	(void) pthread_mutex_lock(&vs_eng_mutex);
180 
181 	vs_eng_total_maxcon = 0;
182 
183 	vs_eng_close_connections();
184 
185 	while (vs_eng_total_inuse > 0 || vs_eng_wait_count > 0)
186 		(void) pthread_cond_wait(&vs_eng_shutdown_cv, &vs_eng_mutex);
187 
188 	(void) pthread_mutex_unlock(&vs_eng_mutex);
189 	(void) pthread_cond_destroy(&vs_eng_cv);
190 	(void) pthread_cond_destroy(&vs_eng_shutdown_cv);
191 }
192 
193 
194 /*
195  * vs_eng_set_error
196  *
197  * If the engine identified in conn (host, port) matches the
198  * engine in vs_engines set or clear the error state of the
199  * engine and update the error statistics.
200  *
201  * If error == 0, clear the error state(0), else set the error
202  * state (1)
203  */
204 void
205 vs_eng_set_error(vs_eng_conn_t *conn, int error)
206 {
207 	int idx = conn->vsc_idx;
208 
209 	(void) pthread_mutex_lock(&vs_eng_mutex);
210 
211 	if (vs_eng_compare(idx, conn->vsc_host, conn->vsc_port) == 0)
212 		vs_engines[idx].vse_error = error ? 1 : 0;
213 
214 	(void) pthread_mutex_unlock(&vs_eng_mutex);
215 }
216 
217 
218 
219 /*
220  * vs_eng_get
221  * Get next available scan engine connection.
222  * If retry != 0 look for a scan engine with no errors.
223  *
224  * Returns: 0 - success
225  *         -1 - error
226  */
227 int
228 vs_eng_get(vs_eng_conn_t *conn, int retry)
229 {
230 	struct timespec tswait;
231 	int idx;
232 
233 	(void) pthread_mutex_lock(&vs_eng_mutex);
234 
235 	/*
236 	 * If no engines connections configured or
237 	 * retry and only one engine configured, give up
238 	 */
239 	if ((vs_eng_total_maxcon <= 0) || (retry && (vs_eng_count <= 1))) {
240 		(void) pthread_mutex_unlock(&vs_eng_mutex);
241 		return (-1);
242 	}
243 
244 	tswait.tv_sec = vs_eng_wait;
245 	tswait.tv_nsec = 0;
246 
247 	while ((vscand_get_state() != VS_STATE_SHUTDOWN) &&
248 	    ((idx = vs_eng_find_next(retry)) == -1)) {
249 		/* If retry and all configured engines have errors, give up */
250 		if (retry && vs_eng_check_errors()) {
251 			(void) pthread_mutex_unlock(&vs_eng_mutex);
252 			return (-1);
253 		}
254 
255 		/* wait for a connection to become available */
256 		vs_eng_wait_count++;
257 		if (pthread_cond_reltimedwait_np(&vs_eng_cv, &vs_eng_mutex,
258 		    &tswait) < 0) {
259 			syslog(LOG_WARNING, "Scan Engine "
260 			    "- timeout waiting for available engine");
261 			vs_eng_wait_count--;
262 			if (vscand_get_state() == VS_STATE_SHUTDOWN)
263 				(void) pthread_cond_signal(&vs_eng_shutdown_cv);
264 			(void) pthread_mutex_unlock(&vs_eng_mutex);
265 			return (-1);
266 		}
267 		vs_eng_wait_count--;
268 	}
269 
270 	if (vscand_get_state() == VS_STATE_SHUTDOWN) {
271 		(void) pthread_cond_signal(&vs_eng_shutdown_cv);
272 		(void) pthread_mutex_unlock(&vs_eng_mutex);
273 		return (-1);
274 	}
275 
276 	conn->vsc_idx = idx;
277 	(void) strlcpy(conn->vsc_engid,  vs_engines[idx].vse_cfg.vep_engid,
278 	    sizeof (conn->vsc_engid));
279 	(void) strlcpy(conn->vsc_host, vs_engines[idx].vse_cfg.vep_host,
280 	    sizeof (conn->vsc_host));
281 	conn->vsc_port = vs_engines[idx].vse_cfg.vep_port;
282 
283 	/* update in use counts */
284 	vs_engines[idx].vse_in_use++;
285 	vs_eng_total_inuse++;
286 
287 	/* add to connections list for engine */
288 	vs_eng_add_connection(conn);
289 
290 	/* update round-robin index */
291 	if (!retry)
292 		vs_eng_next = (idx == VS_SE_MAX) ? 0 : idx + 1;
293 
294 	(void) pthread_mutex_unlock(&vs_eng_mutex);
295 
296 	return (0);
297 }
298 
299 
300 /*
301  * vs_eng_check_errors
302  *
303  * Check if there are any engines, with maxcon > 0,
304  * which are not in error state
305  *
306  * Returns: 1 - all (valid) engines are in error state
307  *          0 - otherwise
308  */
309 static int
310 vs_eng_check_errors()
311 {
312 	int i;
313 
314 	for (i = 0; i < VS_SE_MAX; i++) {
315 		if (vs_engines[i].vse_cfg.vep_maxconn > 0 &&
316 		    (vs_engines[i].vse_error == 0))
317 			return (0);
318 	}
319 
320 	return (1);
321 }
322 
323 
324 /*
325  * vs_eng_find_next
326  *
327  * Returns: -1 no engine connections available
328  *			idx of engine to use
329  */
330 static int
331 vs_eng_find_next(int retry)
332 {
333 	int i;
334 
335 	for (i = vs_eng_next; i < VS_SE_MAX; i++) {
336 		if (vs_engines[i].vse_in_use <
337 		    vs_engines[i].vse_cfg.vep_maxconn) {
338 			if (!retry || (vs_engines[i].vse_error == 0))
339 				return (i);
340 		}
341 	}
342 
343 	for (i = 0; i < vs_eng_next; i++) {
344 		if (vs_engines[i].vse_in_use <
345 		    vs_engines[i].vse_cfg.vep_maxconn) {
346 			if (!retry || (vs_engines[i].vse_error == 0))
347 				return (i);
348 		}
349 	}
350 
351 	return (-1);
352 }
353 
354 
355 /*
356  * vs_eng_release
357  */
358 void
359 vs_eng_release(vs_eng_conn_t *conn)
360 {
361 	int idx = conn->vsc_idx;
362 
363 	/* disconnect */
364 	if (conn->vsc_sockfd != -1) {
365 		(void) close(conn->vsc_sockfd);
366 		conn->vsc_sockfd = -1;
367 	}
368 
369 	(void) pthread_mutex_lock(&vs_eng_mutex);
370 
371 	/* decrement in use counts */
372 	vs_engines[idx].vse_in_use--;
373 	vs_eng_total_inuse--;
374 
375 	/* remove from connections list for engine */
376 	vs_eng_remove_connection(conn);
377 
378 	/* wake up next thread waiting for a connection */
379 	(void) pthread_cond_signal(&vs_eng_cv);
380 
381 	/* if shutdown, send shutdown signal */
382 	if (vscand_get_state() == VS_STATE_SHUTDOWN)
383 		(void) pthread_cond_signal(&vs_eng_shutdown_cv);
384 
385 	(void) pthread_mutex_unlock(&vs_eng_mutex);
386 }
387 
388 
389 /*
390  * vs_eng_add_connection
391  * Add a connection into appropriate engine's connections list
392  */
393 static void
394 vs_eng_add_connection(vs_eng_conn_t *conn)
395 {
396 	vs_eng_conn_t *conn_root;
397 
398 	conn_root = &(vs_engines[conn->vsc_idx].vse_conn_root);
399 	conn->vsc_prev = conn_root;
400 	conn->vsc_next = conn_root->vsc_next;
401 	if (conn->vsc_next)
402 		(conn->vsc_next)->vsc_prev = conn;
403 	conn_root->vsc_next = conn;
404 }
405 
406 
407 /*
408  * vs_eng_remove_connection
409  * Remove a connection from appropriate engine's connections list
410  */
411 static void
412 vs_eng_remove_connection(vs_eng_conn_t *conn)
413 {
414 	(conn->vsc_prev)->vsc_next = conn->vsc_next;
415 	if (conn->vsc_next)
416 		(conn->vsc_next)->vsc_prev = conn->vsc_prev;
417 }
418 
419 
420 /*
421  * vs_eng_close_connections
422  * Close all open connections to abort in-progress scans.
423  */
424 static void
425 vs_eng_close_connections(void)
426 {
427 	int i;
428 	vs_eng_conn_t *conn;
429 
430 	for (i = 0; i < VS_SE_MAX; i++) {
431 		conn = vs_engines[i].vse_conn_root.vsc_next;
432 		while (conn) {
433 			(void) close(conn->vsc_sockfd);
434 			conn->vsc_sockfd = -1;
435 			conn = conn->vsc_next;
436 		}
437 	}
438 }
439 
440 
441 /*
442  * vs_eng_connect
443  * open socket connection to remote scan engine
444  */
445 int
446 vs_eng_connect(vs_eng_conn_t *conn)
447 {
448 	int rc, sock_opt, err_num;
449 	struct sockaddr_in addr;
450 	struct hostent *hp;
451 
452 	if ((conn->vsc_sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
453 		return (-1);
454 
455 	hp = getipnodebyname(conn->vsc_host, AF_INET, 0, &err_num);
456 	if (hp == NULL)
457 		return (-1);
458 
459 	(void) memset(&addr, 0, sizeof (addr));
460 	(void) memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
461 	addr.sin_port = htons(conn->vsc_port);
462 	addr.sin_family = hp->h_addrtype;
463 	freehostent(hp);
464 
465 #ifdef FIONBIO /* Use non-blocking mode for connect. */
466 	rc = nbio_connect(conn, (struct sockaddr *)&addr,
467 	    sizeof (struct sockaddr));
468 #else
469 	rc = connect(conn->vsc_sockfd, (struct sockaddr *)&addr,
470 	    sizeof (struct sockaddr));
471 #endif
472 
473 	sock_opt = 1;
474 
475 	if ((rc < 0) || (vscand_get_state() == VS_STATE_SHUTDOWN) ||
476 	    (setsockopt(conn->vsc_sockfd, IPPROTO_TCP, TCP_NODELAY,
477 	    &sock_opt, sizeof (sock_opt)) < 0) ||
478 	    (setsockopt(conn->vsc_sockfd, SOL_SOCKET, SO_KEEPALIVE,
479 	    &sock_opt, sizeof (sock_opt)) < 0)) {
480 		(void) close(conn->vsc_sockfd);
481 		conn->vsc_sockfd = -1;
482 		return (-1);
483 	}
484 
485 	return (0);
486 }
487 
488 
489 /*
490  * nbio_connect
491  *
492  * Attempt to do a non-blocking connect call.
493  * Wait for a maximum of "vs_connect_timeout" millisec, then check for
494  * socket error to determine if connect successful or not.
495  */
496 #ifdef FIONBIO
497 static int
498 nbio_connect(vs_eng_conn_t *conn, const struct sockaddr *sa, int sa_len)
499 {
500 	struct pollfd pfd;
501 	int nbio, rc;
502 	int soc = conn->vsc_sockfd;
503 	int error, len = sizeof (error);
504 
505 	nbio = 1;
506 	if ((ioctl(soc, FIONBIO, &nbio)) < 0)
507 		return (connect(soc, sa, sa_len));
508 
509 	if ((rc = connect(soc, sa, sa_len)) != 0) {
510 		if (errno == EINPROGRESS || errno == EINTR) {
511 			pfd.fd = soc;
512 			pfd.events = POLLOUT;
513 			pfd.revents = 0;
514 
515 			if ((rc = poll(&pfd, 1, vs_connect_timeout)) <= 0) {
516 				if (rc == 0)
517 					errno = ETIMEDOUT;
518 				rc = -1;
519 			} else {
520 				rc = getsockopt(soc, SOL_SOCKET, SO_ERROR,
521 				    &error, &len);
522 				if (rc != 0 || error != 0)
523 					rc = -1;
524 			}
525 		}
526 	}
527 
528 	nbio = 0;
529 	(void) ioctl(soc, FIONBIO, &nbio);
530 
531 	return (rc);
532 }
533 #endif
534 
535 
536 /*
537  * vs_eng_scanstamp_current
538  *
539  * Check if scanstamp matches that of ANY engine with no errors.
540  * We cannot include engines with errors as they may have been
541  * inaccessible for a long time and thus we may have an old
542  * scanstamp value for them.
543  * If a match is found the scanstamp is considered to be current
544  *
545  * returns: 1 if current, 0 otherwise
546  */
547 int
548 vs_eng_scanstamp_current(vs_scanstamp_t scanstamp)
549 {
550 	int i;
551 
552 	/* if scan stamp is null, not current */
553 	if (scanstamp[0] == '\0')
554 		return (0);
555 
556 	/* if scanstamp matches that of any engine with no errors */
557 	(void) pthread_mutex_lock(&vs_eng_mutex);
558 	for (i = 0; i < VS_SE_MAX; i++) {
559 		if ((vs_engines[i].vse_error == 0) &&
560 		    (vs_icap_compare_scanstamp(i, scanstamp) == 0))
561 			break;
562 	}
563 	(void) pthread_mutex_unlock(&vs_eng_mutex);
564 
565 	return ((i < VS_SE_MAX) ? 1 : 0);
566 }
567 
568 
569 /*
570  * vs_eng_compare
571  * compare host and port with that stored for engine idx
572  *
573  * Returns: 0 - if equal
574  */
575 static int
576 vs_eng_compare(int idx, char *host, int port)
577 {
578 	if (vs_engines[idx].vse_cfg.vep_port != port)
579 		return (-1);
580 
581 	if (strcmp(vs_engines[idx].vse_cfg.vep_host, host) != 0)
582 		return (-1);
583 
584 	return (0);
585 }
586