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