libc: remove old sun4 capabilities
[unleashed.git] / usr / src / cmd / vntsd / listen.c
blobb841aae23fc53bf5c1734f9b9386f4238f93aa24
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
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.
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <sys/types.h>
37 #include <sys/socket.h>
38 #include <netinet/in.h>
39 #include <thread.h>
40 #include <assert.h>
41 #include <signal.h>
42 #include <ctype.h>
43 #include <syslog.h>
44 #include "vntsd.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.
53 static void
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);
68 switch (status) {
70 case VNTSD_SUCCESS:
71 return;
74 case VNTSD_STATUS_ACCEPT_ERR:
75 return;
77 case VNTSD_STATUS_INTR:
78 assert(groupp->status & VNTSD_GROUP_SIG_WAIT);
79 /*FALLTHRU*/
80 case VNTSD_STATUS_NO_CONS:
81 default:
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
89 * thread and exit.
91 groupp->status &= ~VNTSD_GROUP_SIG_WAIT;
92 (void) cond_signal(&groupp->cvp);
93 (void) mutex_unlock(&groupp->lock);
94 thr_exit(0);
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);
111 /* log error */
112 if (status != VNTSD_STATUS_NO_CONS)
113 vntsd_log(status, err_msg);
114 thr_exit(0);
118 /* allocate and initialize listening socket. */
119 static int
120 open_socket(int port_no, int *sockfd)
123 struct sockaddr_in addr;
124 int on;
125 int retries = 0;
128 /* allocate a socket */
129 *sockfd = socket(AF_INET, SOCK_STREAM, 0);
130 if (*sockfd < 0) {
131 if (errno == EINTR) {
132 return (VNTSD_STATUS_INTR);
134 return (VNTSD_ERR_LISTEN_SOCKET);
137 #ifdef DEBUG
138 /* set reuse local socket address */
139 on = 1;
140 if (setsockopt(*sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on))) {
141 return (VNTSD_ERR_LISTEN_OPTS);
143 #endif
145 addr.sin_family = AF_INET;
146 addr.sin_addr.s_addr = (vntsd_ip_addr()).s_addr;
147 addr.sin_port = htons(port_no);
149 /* bind socket */
151 for (; ; ) {
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 */
168 (void) sleep(5);
170 /* woke up by signal? */
171 if (errno == EINTR) {
172 return (VNTSD_STATUS_INTR);
175 retries++;
176 continue;
179 return (VNTSD_ERR_LISTEN_BIND);
183 break;
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 */
199 static int
200 create_console_thread(vntsd_group_t *groupp, int sockfd)
202 vntsd_client_t *clientp;
203 vntsd_thr_arg_t *thr_arg;
204 int rv;
207 assert(groupp);
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))
237 != VNTSD_SUCCESS) {
238 (void) mutex_unlock(&groupp->lock);
239 vntsd_free_client(clientp);
240 return (rv);
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)) {
266 free(thr_arg);
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);
281 /* listen thread */
282 void *
283 vntsd_listen_thread(vntsd_group_t *groupp)
286 int newsockfd;
287 size_t clilen;
288 struct sockaddr_in cli_addr;
289 int rv;
290 int num_cons;
291 vntsd_t *vntsdp;
293 assert(groupp);
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);
306 for (; ; ) {
308 clilen = sizeof (cli_addr);
310 /* listen to the socket */
311 newsockfd = accept(groupp->sockfd, (struct sockaddr *)&cli_addr,
312 &clilen);
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);
321 } else {
322 listen_chk_status(groupp,
323 VNTSD_STATUS_ACCEPT_ERR);
325 continue;
328 /* Check authorization if enabled */
329 if ((vntsdp->options & VNTSD_OPT_AUTH_CHECK) != 0) {
330 rv = auth_check_fd(newsockfd, groupp->group_name);
331 if (rv != B_TRUE) {
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);
336 continue;
340 num_cons = vntsd_chk_group_total_cons(groupp);
341 if (num_cons == 0) {
342 (void) close(newsockfd);
343 listen_chk_status(groupp, VNTSD_STATUS_NO_CONS);
344 continue;
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);
360 /*NOTREACHED*/
361 return (NULL);