xref: /illumos-gate/usr/src/cmd/vntsd/listen.c (revision 3c96341a)
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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
26 
27 /*
28  * Each group has a listen thread. It is created at the time
29  * of a group creation and destroyed when a group does not have
30  * any console associated with it.
31  */
32 
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include <sys/types.h>
38 #include <sys/socket.h>
39 #include <netinet/in.h>
40 #include <thread.h>
41 #include <assert.h>
42 #include <signal.h>
43 #include <ctype.h>
44 #include <syslog.h>
45 #include "vntsd.h"
46 
47 #define	    MAX_BIND_RETRIES		6
48 /*
49  * check the state of listen thread. exit if there is an fatal error
50  * or the group is removed. Main thread will call free_group
51  * to close group socket and free group structure.
52  */
53 static void
54 listen_chk_status(vntsd_group_t *groupp, int status)
55 {
56 	char	    err_msg[VNTSD_LINE_LEN];
57 
58 
59 	D1(stderr, "t@%d listen_chk_status() status=%d group=%s "
60 	    "tcp=%lld group status = %x\n", thr_self(), status,
61 	    groupp->group_name, groupp->tcp_port, groupp->status);
62 
63 	(void) snprintf(err_msg, sizeof (err_msg),
64 	    "Group:%s TCP port %lld status %x",
65 	    groupp->group_name, groupp->tcp_port, groupp->status);
66 
67 
68 	switch (status) {
69 
70 	case VNTSD_SUCCESS:
71 		return;
72 
73 	case VNTSD_STATUS_INTR:
74 		/* signal for deleting group */
75 		assert(groupp->status & VNTSD_GROUP_SIG_WAIT);
76 
77 		/* let main thread know  */
78 		(void) mutex_lock(&groupp->lock);
79 		groupp->status &= ~VNTSD_GROUP_SIG_WAIT;
80 		(void) cond_signal(&groupp->cvp);
81 		(void) mutex_unlock(&groupp->lock);
82 
83 		thr_exit(0);
84 		break;
85 
86 	case VNTSD_STATUS_ACCEPT_ERR:
87 		return;
88 
89 	case VNTSD_STATUS_NO_CONS:
90 	default:
91 		/* fatal error or no console in the group, remove the group. */
92 
93 		(void) mutex_lock(&groupp->lock);
94 
95 		if (groupp->status & VNTSD_GROUP_SIG_WAIT) {
96 			/* group is already in deletion */
97 			(void) mutex_unlock(&groupp->lock);
98 			return;
99 		}
100 
101 		/*
102 		 * if there still is console(s) in the group,
103 		 * the console(s) could not be connected any more because of
104 		 * a fatal error. Therefore, mark the console and notify
105 		 * main thread to delete console and group.
106 		 */
107 		(void) vntsd_que_walk(groupp->conspq,
108 		    (el_func_t)vntsd_mark_deleted_cons);
109 		groupp->status |= VNTSD_GROUP_CLEAN_CONS;
110 
111 		/* signal main thread to delete the group */
112 		(void) thr_kill(groupp->vntsd->tid, SIGUSR1);
113 		(void) mutex_unlock(&groupp->lock);
114 
115 		/* log error */
116 		if (status != VNTSD_STATUS_NO_CONS)
117 			vntsd_log(status, err_msg);
118 		break;
119 	}
120 }
121 
122 /* allocate and initialize listening socket. */
123 static int
124 open_socket(int port_no, int *sockfd)
125 {
126 
127 	struct	    sockaddr_in addr;
128 	int	    on;
129 	int	    retries = 0;
130 
131 
132 	/* allocate a socket */
133 	*sockfd = socket(AF_INET, SOCK_STREAM, 0);
134 	if (*sockfd < 0) {
135 		if (errno == EINTR) {
136 			return (VNTSD_STATUS_INTR);
137 		}
138 		return (VNTSD_ERR_LISTEN_SOCKET);
139 	}
140 
141 #ifdef DEBUG
142 	/* set reuse local socket address */
143 	on = 1;
144 	if (setsockopt(*sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on))) {
145 		return (VNTSD_ERR_LISTEN_OPTS);
146 	}
147 #endif
148 
149 	addr.sin_family = AF_INET;
150 	addr.sin_addr.s_addr = (vntsd_ip_addr()).s_addr;
151 	addr.sin_port = htons(port_no);
152 
153 	/* bind socket */
154 
155 	for (; ; ) {
156 
157 		/*
158 		 * After a socket is closed, the port
159 		 * is transitioned to TIME_WAIT state.
160 		 * It may take a few retries to bind
161 		 * a just released port.
162 		 */
163 		if (bind(*sockfd, (struct sockaddr *)&addr,
164 			    sizeof (addr)) < 0) {
165 
166 			if (errno == EINTR) {
167 				return (VNTSD_STATUS_INTR);
168 			}
169 
170 			if (errno == EADDRINUSE && retries < MAX_BIND_RETRIES) {
171 				/* port may be in TIME_WAIT state, retry */
172 				(void) sleep(5);
173 
174 				/* woke up by signal? */
175 				if (errno == EINTR) {
176 					return (VNTSD_STATUS_INTR);
177 				}
178 
179 				retries++;
180 				continue;
181 			}
182 
183 			return (VNTSD_ERR_LISTEN_BIND);
184 
185 		}
186 
187 		break;
188 
189 	}
190 
191 	if (listen(*sockfd, VNTSD_MAX_SOCKETS) == -1) {
192 		if (errno == EINTR) {
193 			return (VNTSD_STATUS_INTR);
194 		}
195 		return (VNTSD_ERR_LISTEN_BIND);
196 	}
197 
198 	D1(stderr, "t@%d open_socket() sockfd=%d\n", thr_self(), *sockfd);
199 	return (VNTSD_SUCCESS);
200 }
201 
202 /* ceate console selection thread */
203 static int
204 create_console_thread(vntsd_group_t *groupp, int sockfd)
205 {
206 	vntsd_client_t	    *clientp;
207 	vntsd_thr_arg_t	    *thr_arg;
208 	int		    rv;
209 
210 
211 	assert(groupp);
212 	D1(stderr, "t@%d create_console_thread@%lld:client@%d\n", thr_self(),
213 	    groupp->tcp_port, sockfd);
214 
215 	/* allocate a new client */
216 	clientp = (vntsd_client_t *)malloc(sizeof (vntsd_client_t));
217 	if (clientp  == NULL) {
218 		return (VNTSD_ERR_NO_MEM);
219 	}
220 
221 	/* initialize the client */
222 	bzero(clientp, sizeof (vntsd_client_t));
223 
224 	clientp->sockfd = sockfd;
225 	clientp->cons_tid = (thread_t)-1;
226 
227 	(void) mutex_init(&clientp->lock, USYNC_THREAD|LOCK_ERRORCHECK, NULL);
228 
229 	/* append client to group */
230 	(void) mutex_lock(&groupp->lock);
231 
232 	/* check if the group is [being] removed */
233 	if (groupp->status & VNTSD_GROUP_IN_CLEANUP) {
234 		(void) mutex_unlock(&groupp->lock);
235 		vntsd_free_client(clientp);
236 		return (VNTSD_STATUS_NO_CONS);
237 	}
238 
239 
240 	if ((rv = vntsd_que_append(&groupp->no_cons_clientpq, clientp))
241 	    != VNTSD_SUCCESS) {
242 		(void) mutex_unlock(&groupp->lock);
243 		vntsd_free_client(clientp);
244 		return (rv);
245 	}
246 
247 	(void) mutex_unlock(&groupp->lock);
248 
249 	/*
250 	 * allocate thr_arg from heap for console thread so
251 	 * that thr_arg is still valid after this function exits.
252 	 * console thread will free thr_arg.
253 	 */
254 
255 	thr_arg = (vntsd_thr_arg_t *)malloc(sizeof (vntsd_thr_arg_t));
256 	if (thr_arg  == NULL) {
257 		vntsd_free_client(clientp);
258 		return (VNTSD_ERR_NO_MEM);
259 	}
260 	thr_arg->handle = groupp;
261 	thr_arg->arg = clientp;
262 
263 	(void) mutex_lock(&clientp->lock);
264 
265 
266 	/* create console selection thread */
267 	if (thr_create(NULL, 0, (thr_func_t)vntsd_console_thread,
268 		    thr_arg, THR_DETACHED, &clientp->cons_tid)) {
269 
270 		free(thr_arg);
271 		(void) mutex_unlock(&clientp->lock);
272 		(void) mutex_lock(&groupp->lock);
273 		(void) vntsd_que_rm(&groupp->no_cons_clientpq, clientp);
274 		(void) mutex_unlock(&groupp->lock);
275 		vntsd_free_client(clientp);
276 
277 		return (VNTSD_ERR_CREATE_CONS_THR);
278 	}
279 
280 	(void) mutex_unlock(&clientp->lock);
281 
282 	return (VNTSD_SUCCESS);
283 }
284 
285 /* listen thread */
286 void *
287 vntsd_listen_thread(vntsd_group_t *groupp)
288 {
289 
290 	int		newsockfd;
291 	size_t		clilen;
292 	struct		sockaddr_in cli_addr;
293 	int		rv;
294 	int		num_cons;
295 
296 	assert(groupp);
297 
298 	D1(stderr, "t@%d listen@%lld\n", thr_self(), groupp->tcp_port);
299 
300 
301 	/* initialize listen socket */
302 	(void) mutex_lock(&groupp->lock);
303 	rv = open_socket(groupp->tcp_port, &groupp->sockfd);
304 	(void) mutex_unlock(&groupp->lock);
305 	listen_chk_status(groupp, rv);
306 
307 	for (; ; ) {
308 
309 		clilen = sizeof (cli_addr);
310 
311 		/* listen to the socket */
312 		newsockfd = accept(groupp->sockfd, (struct sockaddr *)&cli_addr,
313 			    &clilen);
314 
315 		D1(stderr, "t@%d listen_thread() connected sockfd=%d\n",
316 		    thr_self(), newsockfd);
317 
318 		if (newsockfd <=  0) {
319 
320 			if (errno == EINTR) {
321 				listen_chk_status(groupp, VNTSD_STATUS_INTR);
322 			} else {
323 				listen_chk_status(groupp,
324 				    VNTSD_STATUS_ACCEPT_ERR);
325 			}
326 			continue;
327 		}
328 		num_cons = vntsd_chk_group_total_cons(groupp);
329 		if (num_cons == 0) {
330 			(void) close(newsockfd);
331 			listen_chk_status(groupp, VNTSD_STATUS_NO_CONS);
332 			continue;
333 		}
334 
335 		/* a connection is established */
336 		rv = vntsd_set_telnet_options(newsockfd);
337 		if (rv != VNTSD_SUCCESS) {
338 			(void) close(newsockfd);
339 			listen_chk_status(groupp, rv);
340 		}
341 		rv = create_console_thread(groupp, newsockfd);
342 		if (rv != VNTSD_SUCCESS) {
343 			(void) close(newsockfd);
344 			listen_chk_status(groupp, rv);
345 		}
346 	}
347 
348 	/*NOTREACHED*/
349 	return (NULL);
350 }
351