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 */
open_ipc()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
close_ipc()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 */
slp_send2slpd(const char * msg,char ** reply)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
slp_send2slpd_iov(struct iovec * msg,int iovlen,char ** reply)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 */
get_localhost_sin()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 *
ipc_manage_thr(void * arg __unused)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