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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright (c) 1999-2001 by Sun Microsystems, Inc. 24 * All rights reserved. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <sys/types.h> 32 #include <sys/socket.h> 33 #include <string.h> 34 #include <errno.h> 35 #include <arpa/inet.h> 36 #include <unistd.h> 37 #include <syslog.h> 38 #include <thread.h> 39 #include <synch.h> 40 #include <netinet/in.h> 41 #include <signal.h> 42 #include <slp-internal.h> 43 44 #define IPC_FD_LIFETIME 30 45 46 /* 47 * Cached parameters and thread synchronization 48 */ 49 static int slpdfd; /* cached FD to slpd */ 50 static mutex_t ipc_lock = DEFAULTMUTEX; /* serializes IPC */ 51 52 /* synch for the FD management thread */ 53 static mutex_t ipc_wait_lock = DEFAULTMUTEX; 54 static cond_t ipc_wait_var; 55 static int ipc_used; 56 static int ipc_thr_running; 57 58 static struct sockaddr_in *local_sin; /* slpd addr, set on first use */ 59 60 static SLPError open_ipc(); 61 static void close_ipc(); 62 static void get_localhost_sin(); 63 static void ipc_manage_thr(); 64 65 /* 66 * Locking should be handled by the caller 67 */ 68 static SLPError open_ipc() { 69 int terr; 70 int retries = 0; 71 72 if (slpdfd) 73 return (SLP_OK); 74 75 /* Make sure the local host's sockaddr_in is set */ 76 if (!local_sin) { 77 get_localhost_sin(); 78 if (!local_sin) { 79 slpdfd = 0; 80 return (SLP_INTERNAL_SYSTEM_ERROR); 81 } 82 } 83 84 for (;;) { 85 int errno_kept; 86 87 if ((slpdfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { 88 slp_err(LOG_CRIT, 0, "slp_open_ipc", 89 "could not create socket: %s", strerror(errno)); 90 slpdfd = 0; 91 return (SLP_INTERNAL_SYSTEM_ERROR); 92 } 93 94 95 if (connect(slpdfd, (struct sockaddr *)local_sin, 96 sizeof (*local_sin)) == 0) { 97 break; 98 } 99 100 /* else error condition */ 101 errno_kept = errno; /* in case errno is reset by slp_err */ 102 if (retries++ == 2) { 103 slp_err(LOG_INFO, 0, "slp_open_ipc", 104 "could not connect to slpd: %s", strerror(errno)); 105 if (errno_kept == ECONNREFUSED) 106 slp_err(LOG_INFO, 0, "slp_open_ipc", 107 "is slpd running?"); 108 (void) close(slpdfd); 109 slpdfd = 0; 110 return (SLP_NETWORK_ERROR); 111 } else { 112 /* back off a little */ 113 (void) close(slpdfd); 114 (void) sleep(1); 115 } 116 } 117 118 /* We now know slpd is reachable; start the management thread */ 119 if (!ipc_thr_running) { 120 if ((terr = thr_create( 121 0, NULL, 122 (void *(*)(void *)) ipc_manage_thr, 123 NULL, 0, NULL)) != 0) { 124 slp_err(LOG_CRIT, 0, "slp_open_ipc", 125 "could not start thread: %s", 126 strerror(terr)); 127 return (SLP_INTERNAL_SYSTEM_ERROR); 128 } 129 } 130 ipc_thr_running = 1; 131 132 return (SLP_OK); 133 } 134 135 static void close_ipc() { 136 (void) mutex_lock(&ipc_lock); 137 if (!slpdfd) { 138 (void) mutex_unlock(&ipc_lock); 139 return; 140 } 141 (void) close(slpdfd); 142 slpdfd = 0; 143 (void) mutex_unlock(&ipc_lock); 144 } 145 146 /* 147 * Sends 'msg' to slpd, placing the response in 'reply'. Caller should 148 * free memory associated with 'reply'. All IPC is handled transparantly 149 * by this call. Note that this call is a wrapper for slp_send2slpd_iov. 150 * Returns SLP_NETWORK_ERROR if slpd is unreachable, SLP_OK otherwise. 151 */ 152 SLPError slp_send2slpd(const char *msg, char **reply) { 153 struct iovec iov[1]; 154 iov->iov_base = (caddr_t)msg; 155 iov->iov_len = slp_get_length(msg); 156 157 return (slp_send2slpd_iov(iov, 1, reply)); 158 } 159 160 SLPError slp_send2slpd_iov(struct iovec *msg, int iovlen, char **reply) { 161 SLPError err; 162 int retries = 0; 163 struct msghdr msghdr[1]; 164 struct sigaction new, old; 165 166 *reply = NULL; 167 168 (void) mutex_lock(&ipc_lock); 169 /* is the connection open? */ 170 if (!slpdfd) { 171 if ((err = open_ipc()) != SLP_OK) { 172 (void) mutex_unlock(&ipc_lock); 173 return (err); 174 } 175 } 176 177 /* populate the msghdr for sendmsg */ 178 msghdr->msg_name = NULL; 179 msghdr->msg_namelen = 0; 180 msghdr->msg_iov = msg; 181 msghdr->msg_iovlen = iovlen; 182 msghdr->msg_accrights = NULL; 183 msghdr->msg_accrightslen = 0; 184 185 /* 186 * If slpd has been restarted while this connection is 187 * still open, we will get a SIGPIPE when we try to write 188 * to it. So we need to ignore SIGPIPEs for the duration of 189 * the communication with slpd. 190 */ 191 new.sa_handler = SIG_IGN; 192 new.sa_flags = 0; 193 (void) sigemptyset(&new.sa_mask); 194 (void) sigaction(SIGPIPE, &new, &old); /* preserve old disposition */ 195 196 while (sendmsg(slpdfd, msghdr, 0) == -1) { 197 int errno_kept = errno; 198 199 switch (errno) { 200 case EINTR: 201 case ENOBUFS: 202 case ENOSR: 203 continue; 204 case EBADF: 205 case ECONNRESET: 206 case ENOTCONN: 207 default: 208 (void) mutex_unlock(&ipc_lock); 209 close_ipc(); 210 if (retries++) { 211 slp_err(LOG_CRIT, 0, "slp_send2slpd", 212 "could not talk to slpd: %s", 213 strerror(errno_kept)); 214 err = SLP_NETWORK_ERROR; 215 goto done; 216 } 217 /* try re-opening the connection to slpd */ 218 if (open_ipc() == SLP_OK) { 219 (void) mutex_lock(&ipc_lock); 220 continue; 221 } else { 222 err = SLP_NETWORK_ERROR; 223 goto done; 224 } 225 } 226 } 227 228 err = slp_tcp_read(slpdfd, reply); 229 230 /* 231 * On error slpd may close the socket; there can be a race 232 * condition here where a following call (attempting to reuse 233 * the socket) may send to slpd before it has closed the socket. 234 * To prevent this, we must also close the socket on error. 235 */ 236 if (err == SLP_OK && slp_get_errcode(*reply) != 0) { 237 (void) mutex_unlock(&ipc_lock); 238 close_ipc(); 239 (void) mutex_lock(&ipc_lock); 240 } 241 242 /* notify ipc thread of call */ 243 (void) mutex_lock(&ipc_wait_lock); 244 ipc_used = 1; 245 (void) cond_signal(&ipc_wait_var); 246 (void) mutex_unlock(&ipc_wait_lock); 247 248 (void) mutex_unlock(&ipc_lock); 249 250 done: 251 /* restore original signal disposition for SIGPIPE */ 252 (void) sigaction(SIGPIPE, &old, NULL); 253 return (err); 254 } 255 256 /* 257 * Sets up a sockaddr_in pointing at slpd. 258 * After the first call, the address of slpd is cached in local_sin. 259 * 260 * side effect: local_sin is set to an address for slpd. 261 */ 262 static void get_localhost_sin() { 263 struct sockaddr_in *sin; 264 static mutex_t lhlock = DEFAULTMUTEX; 265 266 (void) mutex_lock(&lhlock); 267 if (local_sin) { 268 (void) mutex_unlock(&lhlock); 269 return; 270 } 271 272 if (!(sin = calloc(1, sizeof (*sin)))) { 273 slp_err(LOG_CRIT, 0, "get_localhost_sin", "out of memory"); 274 goto done; 275 } 276 277 IN_SET_LOOPBACK_ADDR(sin); 278 sin->sin_family = AF_INET; 279 sin->sin_port = htons(SLP_PORT); 280 281 done: 282 local_sin = sin; 283 (void) mutex_unlock(&lhlock); 284 } 285 286 /* 287 * IPC management: the FD to slpd is kept open and cached to improve 288 * performance on successive calls. The IPC management thread waits 289 * on a condition variable; the condition is if an IPC call has been 290 * made. If so, the thread advances the FD's expiration by IPC_FD_LIFETIME 291 * and continues waiting for the next IPC call. After the FD has expired, 292 * the thread closes IPC and shuts itself down. 293 */ 294 static void ipc_manage_thr() { 295 timestruc_t timeout; 296 297 timeout.tv_nsec = 0; 298 (void) mutex_lock(&ipc_wait_lock); 299 ipc_used = 0; 300 301 while (ipc_used == 0) { 302 int err; 303 304 timeout.tv_sec = IPC_FD_LIFETIME; 305 err = cond_reltimedwait(&ipc_wait_var, &ipc_wait_lock, 306 &timeout); 307 308 if (err == ETIME) { 309 /* shutdown */ 310 close_ipc(); 311 ipc_thr_running = 0; 312 (void) mutex_unlock(&ipc_wait_lock); 313 thr_exit(NULL); 314 } else { 315 /* reset condition variable */ 316 ipc_used = 0; 317 } 318 } 319 } 320