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.
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.
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]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * Each group has a listen thread. It is created at the time
28 * of a group creation and destroyed when a group does not have
29 * any console associated with it.
36 #include <sys/types.h>
37 #include <sys/socket.h>
38 #include <netinet/in.h>
46 #define MAX_BIND_RETRIES 6
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.
54 listen_chk_status(vntsd_group_t
*groupp
, int status
)
56 char err_msg
[VNTSD_LINE_LEN
];
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
);
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
);
74 case VNTSD_STATUS_ACCEPT_ERR
:
77 case VNTSD_STATUS_INTR
:
78 assert(groupp
->status
& VNTSD_GROUP_SIG_WAIT
);
80 case VNTSD_STATUS_NO_CONS
:
82 /* fatal error or no console in the group, remove the group. */
84 (void) mutex_lock(&groupp
->lock
);
86 if (groupp
->status
& VNTSD_GROUP_SIG_WAIT
) {
88 * group is already being deleted, notify main
91 groupp
->status
&= ~VNTSD_GROUP_SIG_WAIT
;
92 (void) cond_signal(&groupp
->cvp
);
93 (void) mutex_unlock(&groupp
->lock
);
98 * if there still is console(s) in the group,
99 * the console(s) could not be connected any more because of
100 * a fatal error. Therefore, mark the console and notify
101 * main thread to delete console and group.
103 (void) vntsd_que_walk(groupp
->conspq
,
104 (el_func_t
)vntsd_mark_deleted_cons
);
105 groupp
->status
|= VNTSD_GROUP_CLEAN_CONS
;
107 /* signal main thread to delete the group */
108 (void) thr_kill(groupp
->vntsd
->tid
, SIGUSR1
);
109 (void) mutex_unlock(&groupp
->lock
);
112 if (status
!= VNTSD_STATUS_NO_CONS
)
113 vntsd_log(status
, err_msg
);
118 /* allocate and initialize listening socket. */
120 open_socket(int port_no
, int *sockfd
)
123 struct sockaddr_in addr
;
128 /* allocate a socket */
129 *sockfd
= socket(AF_INET
, SOCK_STREAM
, 0);
131 if (errno
== EINTR
) {
132 return (VNTSD_STATUS_INTR
);
134 return (VNTSD_ERR_LISTEN_SOCKET
);
138 /* set reuse local socket address */
140 if (setsockopt(*sockfd
, SOL_SOCKET
, SO_REUSEADDR
, &on
, sizeof (on
))) {
141 return (VNTSD_ERR_LISTEN_OPTS
);
145 addr
.sin_family
= AF_INET
;
146 addr
.sin_addr
.s_addr
= (vntsd_ip_addr()).s_addr
;
147 addr
.sin_port
= htons(port_no
);
154 * After a socket is closed, the port
155 * is transitioned to TIME_WAIT state.
156 * It may take a few retries to bind
157 * a just released port.
159 if (bind(*sockfd
, (struct sockaddr
*)&addr
,
160 sizeof (addr
)) < 0) {
162 if (errno
== EINTR
) {
163 return (VNTSD_STATUS_INTR
);
166 if (errno
== EADDRINUSE
&& retries
< MAX_BIND_RETRIES
) {
167 /* port may be in TIME_WAIT state, retry */
170 /* woke up by signal? */
171 if (errno
== EINTR
) {
172 return (VNTSD_STATUS_INTR
);
179 return (VNTSD_ERR_LISTEN_BIND
);
187 if (listen(*sockfd
, VNTSD_MAX_SOCKETS
) == -1) {
188 if (errno
== EINTR
) {
189 return (VNTSD_STATUS_INTR
);
191 return (VNTSD_ERR_LISTEN_BIND
);
194 D1(stderr
, "t@%d open_socket() sockfd=%d\n", thr_self(), *sockfd
);
195 return (VNTSD_SUCCESS
);
198 /* ceate console selection thread */
200 create_console_thread(vntsd_group_t
*groupp
, int sockfd
)
202 vntsd_client_t
*clientp
;
203 vntsd_thr_arg_t
*thr_arg
;
208 D1(stderr
, "t@%d create_console_thread@%lld:client@%d\n", thr_self(),
209 groupp
->tcp_port
, sockfd
);
211 /* allocate a new client */
212 clientp
= (vntsd_client_t
*)malloc(sizeof (vntsd_client_t
));
213 if (clientp
== NULL
) {
214 return (VNTSD_ERR_NO_MEM
);
217 /* initialize the client */
218 bzero(clientp
, sizeof (vntsd_client_t
));
220 clientp
->sockfd
= sockfd
;
221 clientp
->cons_tid
= (thread_t
)-1;
223 (void) mutex_init(&clientp
->lock
, USYNC_THREAD
|LOCK_ERRORCHECK
, NULL
);
225 /* append client to group */
226 (void) mutex_lock(&groupp
->lock
);
228 /* check if the group is [being] removed */
229 if (groupp
->status
& VNTSD_GROUP_IN_CLEANUP
) {
230 (void) mutex_unlock(&groupp
->lock
);
231 vntsd_free_client(clientp
);
232 return (VNTSD_STATUS_NO_CONS
);
236 if ((rv
= vntsd_que_append(&groupp
->no_cons_clientpq
, clientp
))
238 (void) mutex_unlock(&groupp
->lock
);
239 vntsd_free_client(clientp
);
243 (void) mutex_unlock(&groupp
->lock
);
246 * allocate thr_arg from heap for console thread so
247 * that thr_arg is still valid after this function exits.
248 * console thread will free thr_arg.
251 thr_arg
= (vntsd_thr_arg_t
*)malloc(sizeof (vntsd_thr_arg_t
));
252 if (thr_arg
== NULL
) {
253 vntsd_free_client(clientp
);
254 return (VNTSD_ERR_NO_MEM
);
256 thr_arg
->handle
= groupp
;
257 thr_arg
->arg
= clientp
;
259 (void) mutex_lock(&clientp
->lock
);
262 /* create console selection thread */
263 if (thr_create(NULL
, 0, (thr_func_t
)vntsd_console_thread
,
264 thr_arg
, THR_DETACHED
, &clientp
->cons_tid
)) {
267 (void) mutex_unlock(&clientp
->lock
);
268 (void) mutex_lock(&groupp
->lock
);
269 (void) vntsd_que_rm(&groupp
->no_cons_clientpq
, clientp
);
270 (void) mutex_unlock(&groupp
->lock
);
271 vntsd_free_client(clientp
);
273 return (VNTSD_ERR_CREATE_CONS_THR
);
276 (void) mutex_unlock(&clientp
->lock
);
278 return (VNTSD_SUCCESS
);
283 vntsd_listen_thread(vntsd_group_t
*groupp
)
288 struct sockaddr_in cli_addr
;
295 D1(stderr
, "t@%d listen@%lld\n", thr_self(), groupp
->tcp_port
);
298 vntsdp
= groupp
->vntsd
;
300 /* initialize listen socket */
301 (void) mutex_lock(&groupp
->lock
);
302 rv
= open_socket(groupp
->tcp_port
, &groupp
->sockfd
);
303 (void) mutex_unlock(&groupp
->lock
);
304 listen_chk_status(groupp
, rv
);
308 clilen
= sizeof (cli_addr
);
310 /* listen to the socket */
311 newsockfd
= accept(groupp
->sockfd
, (struct sockaddr
*)&cli_addr
,
314 D1(stderr
, "t@%d listen_thread() connected sockfd=%d\n",
315 thr_self(), newsockfd
);
317 if (newsockfd
<= 0) {
319 if (errno
== EINTR
) {
320 listen_chk_status(groupp
, VNTSD_STATUS_INTR
);
322 listen_chk_status(groupp
,
323 VNTSD_STATUS_ACCEPT_ERR
);
328 /* Check authorization if enabled */
329 if ((vntsdp
->options
& VNTSD_OPT_AUTH_CHECK
) != 0) {
330 rv
= auth_check_fd(newsockfd
, groupp
->group_name
);
332 D3(stderr
, "t@%d listen@%lld group@%s: "
333 "authorization failure\n", thr_self(),
334 groupp
->tcp_port
, groupp
->group_name
);
335 (void) close(newsockfd
);
340 num_cons
= vntsd_chk_group_total_cons(groupp
);
342 (void) close(newsockfd
);
343 listen_chk_status(groupp
, VNTSD_STATUS_NO_CONS
);
347 /* a connection is established */
348 rv
= vntsd_set_telnet_options(newsockfd
);
349 if (rv
!= VNTSD_SUCCESS
) {
350 (void) close(newsockfd
);
351 listen_chk_status(groupp
, rv
);
353 rv
= create_console_thread(groupp
, newsockfd
);
354 if (rv
!= VNTSD_SUCCESS
) {
355 (void) close(newsockfd
);
356 listen_chk_status(groupp
, rv
);