xref: /illumos-gate/usr/src/cmd/vntsd/listen.c (revision f808c858fa61e7769218966759510a8b1190dfcf)
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 2006 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 /*
48  * check the state of listen thread. exit if there is an fatal error
49  * or the group is removed.
50  */
51 static void
52 listen_chk_status(vntsd_group_t *groupp, int status)
53 {
54 	char	    err_msg[VNTSD_LINE_LEN];
55 
56 
57 	D1(stderr, "t@%d listen_chk_status() status=%d group=%s "
58 	    "tcp=%lld group status = %x\n", thr_self(), status,
59 	    groupp->group_name, groupp->tcp_port, groupp->status);
60 
61 	(void) snprintf(err_msg, sizeof (err_msg),
62 	    "Group:%s TCP port %lld status %x",
63 	    groupp->group_name, groupp->tcp_port, groupp->status);
64 
65 
66 	switch (status) {
67 
68 	case VNTSD_SUCCESS:
69 		return;
70 
71 	case VNTSD_STATUS_INTR:
72 		assert(groupp->status & VNTSD_GROUP_SIG_WAIT);
73 		/* close listen socket */
74 		(void) mutex_lock(&groupp->lock);
75 		(void) close(groupp->sockfd);
76 		groupp->sockfd = -1;
77 
78 		/* let group know */
79 		groupp->status &= ~VNTSD_GROUP_SIG_WAIT;
80 		(void) cond_signal(&groupp->cvp);
81 
82 		(void) mutex_unlock(&groupp->lock);
83 		/* exit thread */
84 		thr_exit(0);
85 		break;
86 
87 	case VNTSD_STATUS_ACCEPT_ERR:
88 		return;
89 
90 	case VNTSD_STATUS_NO_CONS:
91 	default:
92 		/* fatal, exit thread */
93 
94 		(void) mutex_lock(&groupp->lock);
95 		(void) close(groupp->sockfd);
96 		groupp->sockfd = -1;
97 		(void) mutex_unlock(&groupp->lock);
98 		vntsd_log(status, err_msg);
99 		vntsd_clean_group(groupp);
100 
101 		thr_exit(0);
102 		break;
103 	}
104 }
105 
106 /* allocate and initialize listening socket. */
107 static int
108 open_socket(int port_no, int *sockfd)
109 {
110 
111 	struct	    sockaddr_in addr;
112 	int	    on;
113 
114 
115 	/* allocate a socket */
116 	*sockfd = socket(AF_INET, SOCK_STREAM, 0);
117 	if (*sockfd < 0) {
118 		if (errno == EINTR) {
119 			return (VNTSD_STATUS_INTR);
120 		}
121 		return (VNTSD_ERR_LISTEN_SOCKET);
122 	}
123 
124 #ifdef DEBUG
125 	/* set reuse local socket address */
126 	on = 1;
127 	if (setsockopt(*sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on))) {
128 		return (VNTSD_ERR_LISTEN_OPTS);
129 	}
130 #endif
131 
132 	addr.sin_family = AF_INET;
133 	addr.sin_addr.s_addr = (vntsd_ip_addr()).s_addr;
134 	addr.sin_port = htons(port_no);
135 
136 	/* bind socket */
137 	if (bind(*sockfd, (struct sockaddr *)&addr, sizeof (addr)) < 0) {
138 		if (errno == EINTR) {
139 			return (VNTSD_STATUS_INTR);
140 		}
141 		return (VNTSD_ERR_LISTEN_BIND);
142 
143 	}
144 
145 	if (listen(*sockfd, VNTSD_MAX_SOCKETS) == -1) {
146 		if (errno == EINTR) {
147 			return (VNTSD_STATUS_INTR);
148 		}
149 		return (VNTSD_ERR_LISTEN_BIND);
150 	}
151 
152 	D1(stderr, "t@%d open_socket() sockfd=%d\n", thr_self(), *sockfd);
153 	return (VNTSD_SUCCESS);
154 }
155 
156 /* ceate console selection thread */
157 static int
158 create_console_thread(vntsd_group_t *groupp, int sockfd)
159 {
160 	vntsd_client_t	    *clientp;
161 	vntsd_thr_arg_t	    arg;
162 	int		    rv;
163 
164 
165 	assert(groupp);
166 	D1(stderr, "t@%d create_console_thread@%lld:client@%d\n", thr_self(),
167 	    groupp->tcp_port, sockfd);
168 
169 	/* allocate a new client */
170 	clientp = (vntsd_client_t *)malloc(sizeof (vntsd_client_t));
171 	if (clientp  == NULL) {
172 		return (VNTSD_ERR_NO_MEM);
173 	}
174 
175 	/* initialize the client */
176 	bzero(clientp, sizeof (vntsd_client_t));
177 
178 	clientp->sockfd = sockfd;
179 	clientp->cons_tid = (thread_t)-1;
180 
181 	(void) mutex_init(&clientp->lock, USYNC_THREAD|LOCK_ERRORCHECK, NULL);
182 
183 	/* append client to group */
184 	(void) mutex_lock(&groupp->lock);
185 
186 	if ((rv = vntsd_que_append(&groupp->no_cons_clientpq, clientp))
187 	    != VNTSD_SUCCESS) {
188 		(void) mutex_unlock(&groupp->lock);
189 		vntsd_free_client(clientp);
190 		return (rv);
191 	}
192 
193 	(void) mutex_unlock(&groupp->lock);
194 
195 	(void) mutex_lock(&clientp->lock);
196 
197 	/* parameters for console thread */
198 	bzero(&arg, sizeof (arg));
199 
200 	arg.handle = groupp;
201 	arg.arg = clientp;
202 
203 	/* create console selection thread */
204 	if (thr_create(NULL, 0, (thr_func_t)vntsd_console_thread,
205 		    &arg, THR_DETACHED, &clientp->cons_tid)) {
206 
207 		(void) mutex_unlock(&clientp->lock);
208 		(void) mutex_lock(&groupp->lock);
209 		(void) vntsd_que_rm(&groupp->no_cons_clientpq, clientp);
210 		(void) mutex_unlock(&groupp->lock);
211 		vntsd_free_client(clientp);
212 
213 		return (VNTSD_ERR_CREATE_CONS_THR);
214 	}
215 
216 	(void) mutex_unlock(&clientp->lock);
217 
218 	return (VNTSD_SUCCESS);
219 }
220 
221 /* listen thread */
222 void *
223 vntsd_listen_thread(vntsd_group_t *groupp)
224 {
225 
226 	int		newsockfd;
227 	size_t		clilen;
228 	struct		sockaddr_in cli_addr;
229 	int		rv;
230 	int		num_cons;
231 
232 	assert(groupp);
233 
234 	D1(stderr, "t@%d listen@%lld\n", thr_self(), groupp->tcp_port);
235 
236 
237 	/* initialize listen socket */
238 	(void) mutex_lock(&groupp->lock);
239 	rv = open_socket(groupp->tcp_port, &groupp->sockfd);
240 	(void) mutex_unlock(&groupp->lock);
241 	listen_chk_status(groupp, rv);
242 
243 	for (; ; ) {
244 
245 		clilen = sizeof (cli_addr);
246 
247 		/* listen to the socket */
248 		newsockfd = accept(groupp->sockfd, (struct sockaddr *)&cli_addr,
249 			    &clilen);
250 
251 		D1(stderr, "t@%d listen_thread() connected sockfd=%d\n",
252 		    thr_self(), newsockfd);
253 
254 		if (newsockfd <=  0) {
255 
256 			if (errno == EINTR) {
257 				listen_chk_status(groupp, VNTSD_STATUS_INTR);
258 			} else {
259 				listen_chk_status(groupp,
260 				    VNTSD_STATUS_ACCEPT_ERR);
261 			}
262 			continue;
263 		}
264 		num_cons = vntsd_chk_group_total_cons(groupp);
265 		if (num_cons == 0) {
266 			(void) close(newsockfd);
267 			listen_chk_status(groupp, VNTSD_STATUS_NO_CONS);
268 		}
269 
270 		/* a connection is established */
271 		rv = vntsd_set_telnet_options(newsockfd);
272 		if (rv != VNTSD_SUCCESS) {
273 			(void) close(newsockfd);
274 			listen_chk_status(groupp, rv);
275 		}
276 		rv = create_console_thread(groupp, newsockfd);
277 		if (rv != VNTSD_SUCCESS) {
278 			(void) close(newsockfd);
279 			listen_chk_status(groupp, rv);
280 		}
281 	}
282 
283 	/*NOTREACHED*/
284 	return (NULL);
285 }
286