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