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