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