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