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 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 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. 30 */ 31 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" 45 46 #define MAX_BIND_RETRIES 6 47 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 74 case VNTSD_STATUS_ACCEPT_ERR: 75 return; 76 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. */ 83 84 (void) mutex_lock(&groupp->lock); 85 86 if (groupp->status & VNTSD_GROUP_SIG_WAIT) { 87 /* 88 * group is already being deleted, notify main 89 * thread and exit. 90 */ 91 groupp->status &= ~VNTSD_GROUP_SIG_WAIT; 92 (void) cond_signal(&groupp->cvp); 93 (void) mutex_unlock(&groupp->lock); 94 thr_exit(0); 95 } 96 97 /* 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. 102 */ 103 (void) vntsd_que_walk(groupp->conspq, 104 (el_func_t)vntsd_mark_deleted_cons); 105 groupp->status |= VNTSD_GROUP_CLEAN_CONS; 106 107 /* signal main thread to delete the group */ 108 (void) thr_kill(groupp->vntsd->tid, SIGUSR1); 109 (void) mutex_unlock(&groupp->lock); 110 111 /* log error */ 112 if (status != VNTSD_STATUS_NO_CONS) 113 vntsd_log(status, err_msg); 114 thr_exit(0); 115 } 116 } 117 118 /* allocate and initialize listening socket. */ 119 static int 120 open_socket(int port_no, int *sockfd) 121 { 122 123 struct sockaddr_in addr; 124 int on; 125 int retries = 0; 126 127 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); 133 } 134 return (VNTSD_ERR_LISTEN_SOCKET); 135 } 136 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); 142 } 143 #endif 144 145 addr.sin_family = AF_INET; 146 addr.sin_addr.s_addr = (vntsd_ip_addr()).s_addr; 147 addr.sin_port = htons(port_no); 148 149 /* bind socket */ 150 151 for (; ; ) { 152 153 /* 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. 158 */ 159 if (bind(*sockfd, (struct sockaddr *)&addr, 160 sizeof (addr)) < 0) { 161 162 if (errno == EINTR) { 163 return (VNTSD_STATUS_INTR); 164 } 165 166 if (errno == EADDRINUSE && retries < MAX_BIND_RETRIES) { 167 /* port may be in TIME_WAIT state, retry */ 168 (void) sleep(5); 169 170 /* woke up by signal? */ 171 if (errno == EINTR) { 172 return (VNTSD_STATUS_INTR); 173 } 174 175 retries++; 176 continue; 177 } 178 179 return (VNTSD_ERR_LISTEN_BIND); 180 181 } 182 183 break; 184 185 } 186 187 if (listen(*sockfd, VNTSD_MAX_SOCKETS) == -1) { 188 if (errno == EINTR) { 189 return (VNTSD_STATUS_INTR); 190 } 191 return (VNTSD_ERR_LISTEN_BIND); 192 } 193 194 D1(stderr, "t@%d open_socket() sockfd=%d\n", thr_self(), *sockfd); 195 return (VNTSD_SUCCESS); 196 } 197 198 /* ceate console selection thread */ 199 static int 200 create_console_thread(vntsd_group_t *groupp, int sockfd) 201 { 202 vntsd_client_t *clientp; 203 vntsd_thr_arg_t *thr_arg; 204 int rv; 205 206 207 assert(groupp); 208 D1(stderr, "t@%d create_console_thread@%lld:client@%d\n", thr_self(), 209 groupp->tcp_port, sockfd); 210 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); 215 } 216 217 /* initialize the client */ 218 bzero(clientp, sizeof (vntsd_client_t)); 219 220 clientp->sockfd = sockfd; 221 clientp->cons_tid = (thread_t)-1; 222 223 (void) mutex_init(&clientp->lock, USYNC_THREAD|LOCK_ERRORCHECK, NULL); 224 225 /* append client to group */ 226 (void) mutex_lock(&groupp->lock); 227 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); 233 } 234 235 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); 241 } 242 243 (void) mutex_unlock(&groupp->lock); 244 245 /* 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. 249 */ 250 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); 255 } 256 thr_arg->handle = groupp; 257 thr_arg->arg = clientp; 258 259 (void) mutex_lock(&clientp->lock); 260 261 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)) { 265 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); 272 273 return (VNTSD_ERR_CREATE_CONS_THR); 274 } 275 276 (void) mutex_unlock(&clientp->lock); 277 278 return (VNTSD_SUCCESS); 279 } 280 281 /* listen thread */ 282 void * 283 vntsd_listen_thread(vntsd_group_t *groupp) 284 { 285 286 int newsockfd; 287 size_t clilen; 288 struct sockaddr_in cli_addr; 289 int rv; 290 int num_cons; 291 vntsd_t *vntsdp; 292 293 assert(groupp); 294 295 D1(stderr, "t@%d listen@%lld\n", thr_self(), groupp->tcp_port); 296 297 298 vntsdp = groupp->vntsd; 299 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); 305 306 for (; ; ) { 307 308 clilen = sizeof (cli_addr); 309 310 /* listen to the socket */ 311 newsockfd = accept(groupp->sockfd, (struct sockaddr *)&cli_addr, 312 &clilen); 313 314 D1(stderr, "t@%d listen_thread() connected sockfd=%d\n", 315 thr_self(), newsockfd); 316 317 if (newsockfd <= 0) { 318 319 if (errno == EINTR) { 320 listen_chk_status(groupp, VNTSD_STATUS_INTR); 321 } else { 322 listen_chk_status(groupp, 323 VNTSD_STATUS_ACCEPT_ERR); 324 } 325 continue; 326 } 327 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; 337 } 338 } 339 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; 345 } 346 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); 352 } 353 rv = create_console_thread(groupp, newsockfd); 354 if (rv != VNTSD_SUCCESS) { 355 (void) close(newsockfd); 356 listen_chk_status(groupp, rv); 357 } 358 } 359 360 /*NOTREACHED*/ 361 return (NULL); 362 } 363